美文网首页
mysql 数据倾斜导致扫描数过多

mysql 数据倾斜导致扫描数过多

作者: 放开那个BUG | 来源:发表于2021-04-19 23:06 被阅读0次

    一、事情过程

    今天突然有一个平时很正常的 sql 跑了2000多次,并且每次都跑2秒,差点把数据库搞挂了。

    select o.* from
             money o
            inner join moneyPool p
            where o.pool_id = p.id
            and p.code = 'xushu'
            and o.status = 'APPLIED'
            and o.transOrderNo = 'xushu_good_luck' 
    

    二、分析

    首先,我们要明白 join 的执行逻辑。不管是 left join、right join、inner join,join 的执行都分为驱动表、被驱动表。explain 的时候,第一行为驱动表,后面的为被驱动表。

    以上表为例,money 有 id、pool_id、status、transOrderNo 字段,money 有联合索引 un_ps(pool_id, status);moneyPool 有 id、code 字段,moneyPool 有索引 code。moneyPool 使用自己的 id 与 money 的 pool_id 联系。所以整个查询过程如下:


    查询过程

    1.首先在驱动表 moneyPool 中根据 code = 'xushu' 进行查询,因为 code 有索引,所以很快。
    即 sql:select * from moneyPool where code = 'xushu'
    2.然后根据找出的 moneyPool 数据找出自己的 id,再使用 id 在 money 表中进行查询。因为 money有联合索引 un_ps(pool_id, status),所以会选中 un_ps(pool_id, status) 进行查询,即 sql:select * from money where pool_id = xx and status = 'APPLIED' and transOrderNo = 'xushu_good_luck'。因为 pool_id = xx 即 code = 'xushu' 的数据量比较大,所以虽然走了索引,但是还需要回表才能找到条件为 transOrderNo = 'xushu_good_luck' 的。最后需要扫描70w 行,导致结果比较慢。
    3.筛选完后,将结果集合并,然后得出最后的结果

    这个 sql 最大的问题是因为每次查询都需要回表 money 筛选,如果数据量小,其实问题不大,但偏偏满足 pool_id = xx 即 code = 'xushu' 的数据量有70w行,导致扫描了太多的行数,所以速度变慢。如果 un_ps 联合索引中,已经能筛选出 transOrderNo = 'xushu_good_luck' 的数据,那么最后只需要回表一行,速度大大增加。

    所以,终极解决方案:加上 un_ps(pool_id, status, transOrderNo ) 联合索引。

    3、总结

    其实还有一个解决方案,在 money 表加区分度高的索引。比如 money 表有一个 transOrderNo 字段,一般编号都是唯一的,如果 transOrderNo 加索引的话,mysql 选取索引的时候就不会选取 un_ps(pool_id, status) 联合索引,而会选 transOrderNo 索引,因为这时候扫描的行数大大减小。

    相关文章

      网友评论

          本文标题:mysql 数据倾斜导致扫描数过多

          本文链接:https://www.haomeiwen.com/subject/tmuwlltx.html