关系型数据库工作原理-查询优化器之循环嵌套连接(12)

本文翻译自Coding-Geek文章:《 How does a relational database work》。原文链接:http://coding-geek.com/how-databases-work/#Buffer-Replacement_strategies

本文翻译了如下章节, 介绍数据库查询优化器的循环嵌套连接的实现原理:

这里写图片描述

连接操作-Join operators

通过上一章的学习,我们已知如何获取数据,现在我们来做数据的连接。

下面我将介绍3中常用的数据库表连接操作:归并连接、哈希连接和循环嵌套连接

在此之前我需要介绍两个新名词内连接对象(inner relation)和外连接对象(outer relation)。连接的对象可以是:
1. 一张表
2. 一个索引
3. 前一次操作产生的中间结果(例如:前一个连接产生的结果)。

当你连接两个对象时,不同的算法管理这两个连接对象的方式有很大差异。后面的章节,我们假设:
1. 外连接对象是参与连接的左数据集(译者注:表、视图等)。
2. 内连接对象是右数据集。

例如:A连接B,A是左数据集,B是右数据集。大多数情况,A连接B与B连接A的代价是一样的。

在这一章节,我假定A集合中有N个元素,B集合有M个元素。数据库在数据统计阶段已经计算出了A和B包含的数据条数。Notes:N和M是连接操作的基数。

循环嵌套连接-Nested loop join

循环嵌套连接是最简单的一种连接操作(译者注:笛卡尔积)。

这里写图片描述

其基本思想是:
1. 循环遍历外连接对象的每一条记录。
2. 查找内连接对象的所有记录,看是否存在匹配的记录(做连接操作)。

它的伪码是这样的:

nested_loop_join(array outer, array inner)
  for each row a in outer
    for each row b in inner
      if (match_join_condition(a,b))
        write_result_in_output(a,b)
      end if
    end for
   end for

因为它是两层for循环,时间复杂度是O(N*M)。

考虑磁盘I/O的开销, 对外连接对象的N条记录的每一条,内层循环都需要读取内连接对象的M条记录。这个算法总共需要从磁盘读取N+N*M条记录。但是,如果内连接对象的数据量很小,可以把它的数据一次性读到内存,这样就仅需要读取M+N条记录。

基于这种方式,内连接对象必须是最小的一个数据集合,这样它就有更大可能性一次加载到内存中。

考虑时间开销,哪个做内连接对象都没有差异;但考虑磁盘I/O的话,两个连接对象最好都只读取一次。当然,内连接对象可以用索引代替,这样可以从磁盘读取更少的数据(性能自然更好)。

这个算法非常简单,这里提供算法的另外一种实现方式;它在内连接对象集太大而不能全部加载到内存的情况下,尽可能的减少磁盘I/O开销。其基本思路是这样的:
1. 不使用从两个连接集合中逐条读取数据的方式。
2. 你可以从两个对象集中批量读取两捆数据到内存(译者注:比如说一次读取一万条)。
3. 比较两捆数据中数据,并保存匹配的数据结果。
4. 然后加载新的两捆数据到内存做比较。
5. 继续做这样的操作直到所有的数据比较完。

下面是算法伪码:

// improved version to reduce the disk I/O.
nested_loop_join_v2(file outer, file inner)
  for each bunch ba in outer
  // ba is now in memory
    for each bunch bb in inner
        // bb is now in memory
        for each row a in ba
          for each row b in bb
            if (match_join_condition(a,b))
              write_result_in_output(a,b)
            end if
          end for
       end for
    end for
   end for

这种算法实现,时间复杂度不会变,但是磁盘I/O降下来了。
1. 前一种算法种需要访问N+N*M次磁盘,因为每读取一条记录就要访问一次磁盘。
2. 后一种算法,磁盘的访问次数变成了number_of_bunches_for(outer)+ number_of_ bunches_for(outer)* number_of_ bunches_for(inner)。
3. 如果你增加每捆数据的条数(译者注:增加每一次读取的数据条数),访问磁盘的次数还会降低(译者注:视内存大小而定。超过一定阈值后性能反而降低,实际开发经验是一次拿2000条数据)。

发布了113 篇原创文章 · 获赞 183 · 访问量 29万+

猜你喜欢

转载自blog.csdn.net/ylforever/article/details/79830865