欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

MySQL中Nested-Loop Join算法小结

程序员文章站 2024-02-25 20:31:09
 不知不觉的玩了两年多的mysql,发现很多人都说mysql对比oracle来说,优化器做的比较差,其实某种程度上来说确实是这样,但是毕竟mysql才到5.7版本...

 不知不觉的玩了两年多的mysql,发现很多人都说mysql对比oracle来说,优化器做的比较差,其实某种程度上来说确实是这样,但是毕竟mysql才到5.7版本,oracle都已经发展到12c了,今天我就看了看mysql的连接算法,嗯,现在来说还是不支持hash join,只有nested-loop join,那今天就总结一下我学习的心得吧。

     nested-loop join基本算法实现,伪代码是这样:

for each row in t1 matching range {
 for each row in t2 matching reference key {
  for each row in t3 {
   if row satisfies join conditions,
   send to client
  }
 }
}

     这段代码很简单,虽然我也不怎么会写代码,但是我还是看得懂的。这里假设有三张表,t1, t2, t3,这段代码,分别会展现出explain计划里的range, ref和all,表现在sql执行计划层里,t3就会进行一次全表扫描,我今天在这个地方看到了一个很妖的优化sql方法,straight-join:,其中提到了驱动表的概念,那么对应过来,驱动表就是伪代码里的t3表,博文里说mysql会自动选择结果集最小的表作为驱动表,作为算法分析,这样选择驱动表确实是消耗最小的办法。那么这里还提到了,通过缩小驱动表结果集进行连接优化,那么根据这个算法来看,结果集较小的驱动表确实可以使循环次数减少。

     当然了,mysql自己在这个算法基础上,演进出了block nested-loop join算法,其实基本上和上面的算法没有区别,伪代码如下:

for each row in t1 matching range {
 for each row in t2 matching reference key {
  store used columns from t1, t2 in join buffer
  if buffer is full {
   for each row in t3 {
    for each t1, t2 combination in join buffer {
     if row satisfies join conditions,
     send to client
    }
   }
   empty buffer
  }
 }
}

if buffer is not empty {
 for each row in t3 {
  for each t1, t2 combination in join buffer {
   if row satisfies join conditions,
   send to client
  }
 }
}

     这个算法,将外层循环的数据缓存在join buffer中,内层循环中的表回合buffer中的数据进行对比,从而减少循环次数,这样便可以提高效率。官网上有个example,我有点没有看明白:如果有10行被缓存到了buffer里,这10行被传给了内层循环,内层循环的所有行都会和buffer中的这10行进行对比。原文是这样的:  

for example, if 10 rows are read into a buffer and the buffer is passed to the next inner loop, each row read in the inner loop can be compared against all 10 rows in the buffer
      如果s指的是t1, t2组合在缓存中的大小,c是这些组合在buffer中的数量,那么t3表被扫描的次数应该是:

      (s * c)/join_buffer_size + 1

     根据这个算式,join_buffer_size越大,扫描的次数越小,如果join_buffer_size到了能缓存所有之前的行组合,那么这时就是性能最好的时候,之后再增大也就没有什么效果了。

在有索引的情况下,mysql会尝试去使用index nested-loop join算法,在有些情况下,可能join的列就是没有索引,那么这时mysql的选择绝对不会是最先介绍的simple nested-loop join算法,因为那个算法太粗暴,不忍直视。数据量大些的复杂sql估计几年都可能跑不出结果,如果你不信,那就是too young too simple。或者inside君可以给你些sql跑跑看。

simple nested-loop join算法的缺点在于其对于内表的扫描次数太多,从而导致扫描的记录太过庞大。block nested-loop join算法较simple nested-loop join的改进就在于可以减少内表的扫描次数,甚至可以和hash join算法一样,仅需扫描内表一次。