前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线上mysql出现Block Nested-Loop Join问题

线上mysql出现Block Nested-Loop Join问题

作者头像
公众号-利志分享
发布2022-04-25 08:59:43
2.1K0
发布2022-04-25 08:59:43
举报
文章被收录于专栏:利志分享

最近线上遇到一个问题,后台一个查询把服务给整挂了,然后找了dba看了下sql慢查询,我们explain一下结果。

一个连表查询出现了:Using join buffer (Block Nested Loop)重新复习一下资料,整理下经验。官方资料如下:

https://dev.mysql.com/doc/refman/5.7/en/nested-loop-joins.html

mysql涉及到的join算法:Nested-Loop Join 和 Block Nested-Loop Join


关于:Nested-Loop Join

在mysql中,Nested-Loop Join是嵌套循环连接,看下官方例子:

select t1.*,t2.*,t3.* from t1 left join t2 on t1.id = t2.id left join t3 on t2.id = t3.id;

关于这个sql,使用嵌套循环连接,则实现算法如下:

代码语言:javascript
复制
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 次查询,其实也就是我们说的笛卡尔积。


关于:Block Nested-Loop Join,是Nested-Loop Join的一种优化,叫缓存块嵌套循环连接,缓存嵌套循环连接是通过一次性缓存多条数据,把参与查询的缓存的列缓存到join buffer中,然后拿join buffer里面的数据匹配和内层数据进行匹配,从而减少内层循环的次数。

先简单看下join buffer 说明

  • join buffer size默认是256k,可以配置
  • join buffer会缓存所有参与查询的列而不是只有join的列 我们再看下Nested-Loop Join实现的伪代码:
代码语言:javascript
复制
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 join 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
    }
  }
}

这个查询,如果S指的是t1, t2组合在缓存中的大小,C是这些组合在buffer中的数量,那么t3表被扫描的次数是:(S * C)/join_buffer_size + 1,这个总的查询次数则:t1 * t2 次数 + t3 被扫描次数。

了解了join的原理,我们再来回来看线上的这个问题。我们有两个表:

一个打分表,一个考试表。我们的需求是查询考试打过分的学生列表,查询考试未打过分列表。然后我们写了两个sql,如下:

查询打分的sql:

explain select a.*,b.* from exam a left join score b on a.id = b.exam_id where b.score is not null order by a.id desc limit 10;

查询未打分的sql:

select a.*,b.* from exam a left join score b on a.id = b.exam_id where b.score is not null order by a.id desc limit 10;

我们对两个sql都进行explain

查询打分的没有出现嵌套循环连接,因为使用到索引,mysql已经知道join的数据没有不用再扫描。查询未打分的出现嵌套循环连接,mysql没使用到索引,mysql join的部分没有数据会扫描b表所有的数据。我们线上当时考试表是50000数据,但是打分的表才700多条数据,由于查询b表的时候是扫描b的所有数据,所以造成线上造成了25000000多次扫描。对线上的连接表,我们一定要慎重,避免出现嵌套循环连接。


关于这次mysql问题总结:

  • join表,尽量用小结果集去连接大结果集,减少外出循环数据量,从而减少内层循环次数。
  • join表,如果实在需要大结果集连接小结果集,我们考虑先把大结果集和小结果集是否能够使用子查询来结果,当然这个还是要看需求是怎么样的,不一定我这里的子查询和你的需求是一样的,不能一概而论,但是只要出现嵌套查询连接,我们一定要优化,避免出现这种。(我们当时线上查询未打分的就通过子查询来处理了。我们的最后子查询:select * from exam where id not in (select exam_id from score) order by id desc limit 10;)
  • 可以考虑控制join buffer size 的大小。
  • jon连表,每个sql一定要记住用explain 分析一下。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 利志分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档