首页 > 数据库 >SQL JOIN的常见连接算法(转载)

SQL JOIN的常见连接算法(转载)

时间:2023-04-21 09:03:52浏览次数:49  
标签:Join 算法 内存 哈希 SQL JOIN 数据 连接

原文:https://zhuanlan.zhihu.com/p/495442432

在数据库和大数据领域,通过SQL中的JOIN连接将两个及两个以上的表(或中间表、视图、物化视图)中的数据 按指定的连接条件关联起来,是很常用也很方便的操作。 我们前面学习了JOIN有多种常见连接方式如内连接INNER JOIN、左外连接LEFT JOIN等,今天来学习一下连接操作具体是如何实现的,有哪些常见的连接算法。

首先说明一下容易让人犯迷糊的概念:Join的连接方式通常也叫做连接类型 Join Types,而Join的连接算法则称为连接方法 Join Methods。 为了方便叙述,先假设一个连接场景:有a、b两个表,分别有Ra、Rb 行数据,使用一个等值连接条件做内连接,即 a JOIN b ON a.c1 = b.c2。

关注公众号大数据研学社:BigDataRLC,第一时间看最新文章。

嵌套循环连接 NESTED LOOP JOIN

嵌套循环连接 NESTED LOOP JOIN 简称NLJ,顾名思义,他就是一个两层的循环,有简单版本和几个优化的变种。

 

动图封面  

NESTED LOOP JOIN示意图,图源见水印。

Simple Nested Loop Join

简单嵌套循环连接 Simple Nested Loop Join是最简单的一种连接实现算法,外层循环遍历a表,内层循环则遍历b表,如果满足连接条件,则组合两个表的列输出,最终生成连接后的临时表。 这种连接算法效率比较低下,其复杂度表示为: O(Ra * Rb)。 伪代码如下:

for aRow in a
  for bRow in b
    if aRow.c1 equals bRow.c2
      combine aRow and bRow
      output abRow

Index Nested Loop Join

索引嵌套循环连接 Index Nested Loop Join的优化思路是利用索引减少内层表的循环次数,通过外层表的匹配列的值直接与内层表匹配列的索引进行匹配,再遍历匹配上的记录进行匹配、合并、输出。该算法避免和内层表的每条记录去进行比较,需要内层表的匹配列存在索引,复杂度可表示为: O(Ra * b表索引高度)。 伪代码如下:

for aRow in a
  find bRows by index b.c2 with value aRow.c1
  for bRow in bRows
    if aRow.c1 equals bRow.c2
      combine aRow and bRow
      output abRow

Block Nested Loop Join

分块嵌套循环连接 Block Nested Loop Join(简称 BNL)的优化思路是利用缓存减少内层表的循环次数,通过缓存外层表的多条数据到Join Buffer里(通常是个HASH表),然后批量与内层表的数据进行匹配、合并、输出。当内层表的匹配列不存在索引时可以使用Block Nested Loop Join,复杂度可表示为: O(a表的Buffer个数 * Rb),其中Buffer个数为表中全部数据加载到内存需要的内存页数量 / 可用于缓存的内存能容纳的内存页的个数 。 伪代码如下:

var joinBuffers
for aRow in a
  if joinBuffers not full
    add aRow to joinBuffers
  else 
    for bRow in bRows
      find aRow in joinBuffers and if aRow.c1 equals bRow.c2
        combine aRow and bRow
        output abRow
    clean joinBuffers

Batched Key Access Join

批量索引访问连接Batched Key Access Join(简称BKA)是结合了分块嵌套循环连接 Block Nested Loop Join 和索引嵌套循环连接 Index Nested Loop Join两种方案的优点,通过先对外层表数据做攒批再批量查询,内层表的索引的方式进一步提高效率,复杂度可表示为: O(a表的Buffer个数 * b表索引高度)。

排序合并连接 SORT MERGE JOIN

排序合并连接SORT MERGE JOIN 也是嵌套循环连接 NESTED LOOP JOIN 的一种变体。 该算法要求连接中的两个数据集有序,如果数据集尚未排序,则需要先对它们进行排序,这就是SORT操作。对于第一个数据集中的每一行,在第二个数据集中找到一个起始行,该起始行为前一次迭代中的到达的位置,然后读取第二个数据集,直到找到一个不匹配的行时停止,再对第一个数据集中的下一行进行相同的操作,这就是MERGE 操作。

动图封面  

SORT MERGE JOIN示意图,图中的两个数据集都是排序后的,图源见水印。

 

哈希连接 HASH JOIN

哈希连接 HASH JOIN 使用两个表(数据集)中较小的一个连接键上构建内存中的哈希表,然后遍历更大的表(数据集),探测哈希表以找到满足连接条件的行。其中构建内存哈希表的数据集成为构建表(Build Table),而用来探测的表称为探测表(Probe Table)。

动图封面  

HASH JOIN示意图,图源见水印。

简单哈希连接

当构建表中数据量较小,小到足以放入内存哈希表中时,HASH JOIN效率最高,相当于只分别读取了一次连接的两个表,复杂度可表示为: O(Ra + Rb)。当构建表不足以全量放入内存哈希表时,需要用到接下来介绍的几种优化方案。

分批哈希连接

将构建表中的数据分批,构建多批哈希表,每一批再遍历探测表;该方案和前面的Block Nested Loop Join类似,复杂度也一样。

哈希桶溢出磁盘连接

首先,对构建表进行完整扫描,对连接列做哈希后首先尝试放入内存哈希表,如果内存表满了则将内存哈希表中当前最大的桶的数据放入磁盘上的临时空间,新的行如果属于该最大的桶则直接写入临时空间中的对应桶,否则写入内存表中对应的桶;最中可能会有多个桶存储在磁盘上,剩下的桶存储在内存中。

然后,遍历读取探测表,用相同的哈希函数计算连接列的哈希,探测内存哈希表中是否存在该哈希对应的桶,如果存在则和内存中的数据做连接并返回即可,否则对应桶是在磁盘中,用上面相同方式将该数据存放在磁盘上的单独探测表的桶中。

最后,逐一读取磁盘上临时空间中探测表的数据桶,并和磁盘临时空间中构建表对应的数据桶中的数据进行连接后返回,复杂度可以表示为: O(Ra + Rb + 溢出到磁盘的a表行数 + 溢出到磁盘的b表行数)。如果可以并发执行各个桶的连接操作将会有更快的效率。

分布式哈希连接

对于支持多节点并发计算的系统,可以将构建表和探测表的数据都按连接条件做哈希进行分区,然后发送到多个计算节点上分别对每个分区的数据进行连接操作,最后再合并各节点的连接结果。

分布式哈希优化可以利用多个节点的内存,而不需要或者只有很少量数据需要将构建表的数据溢出到磁盘上,同时可以充分利用多节点的CPU资源进行并发计算,因此可用实现大数据量表的连接计算。特别是在分布式数据仓库系统中,数据本身就可以按连接条件均匀分布在多个节点上存储,无需在节点间重新分布数据并做数据传输,连接的效率最高,复杂度可以达到: O((Ra + Rb)/节点数 )。

参考资料

标签:Join,算法,内存,哈希,SQL,JOIN,数据,连接
From: https://www.cnblogs.com/wangbin2188/p/17339091.html

相关文章

  • 将MySQL当中的数据表在PHP当中转换成数组并打印,为什么没有显示到网页上面呢?
    如果你已经成功将MySQL中的数据表转换成了PHP数组,并且没有在网页上看到输出,那么可能是因为你没有将数组中的数据正确地渲染到HTML代码中。以下是一个简单的示例代码,演示如何将PHP数组转换为HTML表格并输出到网页上:phpCopycode<?php//连接到MySQL数据库$servername="local......
  • MYSQL---主从同步概述与配置
    一、MYSQL主从同步概述1、什么是MySQL主从同步?实现数据自动同步的服务结构主服务器(master):接受客户端访问连接从服务器(slave):自动同步主服务器数据2、主从同步原理Maste:启用binlog日志Slave:Slave_IO:复制master主机binlog日志文件的SQL命令到本机的relay-log(中继日志......
  • 通过docker启动mysql
    一、启动mysql1.下载mysqldockerpullmysql:5.7#具体可以去dockerhub中查找想要的版本2.启动mysqldockerrun-d-p3306:3306mysql:5.7--namemysql01-eMYSQL_ROOT_PASSWORD="123"-v/data/mysql/datadir:/var/lib/mysql-v/data/mysql/my.cnf:/etc/......
  • springboot启动自动执行sql脚本
    一:packagecom.lianzhu.bigdata.config;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.CommandLineRunner;importorg.springframework.boot.autoconfigure.EnableAutoConfigurati......
  • QuHai互联科技 算法题部分
    11、实现计算第n个斐波那契数12、给定一个字符串编码规则,如输入字符串”Y3E12S!3”,字母后面的数字表示该字符重复几次,如果字符后没有数字则表示一个字符,最终输出转码后的字符串’YYYEEEEEEEEEEEES!!!’。试写出转码的函数,编程语言不限。13、简述你所了解的两种或以上排序算法......
  • mysql数据库学习1-cmd中乱码怎么办
    由于今天学习orcal,于是便顺便看下mysql,不看不知道,一看吓一跳,自己竟然不知道怎么用cmd登陆mysql。荒废了如此!首先,我们在cmd中要输入用户名和密码,此处的登陆方式不同于sqlplus;mysql-u用户名-p密码在查看当前用户下的表列表时,发现有几个是乱码,因为实在navicat中创建的,所以在编......
  • 扎实打牢数据结构算法根基,从此不怕算法面试系列之week01 02-09 测试算法时间复杂度性
    1、数组生成器测试算法性能肯定不能自己手动声明创建数组了,在现代计算机上,对于O(n)级别的算法,都需要10W级别以上的数据才能看到性能,我们肯定不能手动声明10W个元素的数组吧?所以,创建数组生成器。这里,自己创建一个数组生成器——ArrayGenerator。packagecom.mosesmin.datastruc......
  • ubuntu 安装挂载mysql
    因为有两台电脑,所以准备把ubuntu电脑作成对外服务提供,各种数据库,中间件都使用docker安装管理,然后挂载配置和日志到本地,提供给另一台电脑的对外服务。安装dockersudoaptsearchdockersudoaptinstalldocker.io看到很多安装都繁琐,我也还是在初步使用,如上安装暂时没有发现......
  • 玩一下mysql
     我电脑版本的mysql是5.7.29,此时InnoDB已经是默认的存储引擎存储引擎是基于表的,而不是数据库MyISAM和InnoDB有什么区别?MyISAM不支持事务和行级锁,不支持外键,最大缺陷为崩溃后无法安全恢复。Mysql日志:常见的日志都有什么用?(针对InnoDB引擎)错误日志、二进制日志、一般查询日志......
  • 学习十大排序算法(1)——选择排序【实现方法c语言】
    十大排序算法第一节——选择排序复制代码直接滑到最后!!!选择排序就是找到(最大或者)最小元素,放到最开始的位置,然后就是在没有排序的序列中找到最小的排在已经排好的序列之后,直至没有排数列排完。(自己的理解)大概解释代码其中的细节:第6行中的sizeof的用法是求出括号里面......