暂无图片
暂无图片
6
暂无图片
暂无图片
暂无图片

MySQL优化-生产实践,改写JOIN顺序性能提升7倍

原创 chengang 2025-12-25
779

1、问题复现

今天优化一个多表JOIN语句,调整顺序后,使性能提升7倍,这个提升在我以往文章中算是很低的提升了,但在实际性能优化过程中,不要只去优化那种能提升十倍,百倍的SQL, 哪怕是一个SQL要执行5ms优化到3ms也是有意义的,而且大多数优化均是小幅提升,特别是那种执行频繁的SQL,优化几ms对性能也是极大的提升。

经过简化后的慢SQL如下

select * from a left join b on a.xx=b.xx left join c on b.xx= c.xx left join d on c.xx = d.xx left join e on d.xx = e.xx left join e1 on d.xx = e1.xx left join e2 on c.xx =e2.xx left join e3 on d.xx = e3.xx left join e4 on c.xx =e4.xx left join f on b.xx = f.xx left join g on f.xx = g.xx where a.col = ? and b.col =? and f.col =? and g.col =?

该SQL执行时间为16.7秒

2、问题排查

a、排查第一步 查看执行计划

执行计划如下
f2c27da2e3ed43c29d16572218c3d0ce.png

从执行计划可以看到。符合条件的驱动表行数=51758,
但在join第二张表后数据膨胀到了 434939行
join 第九张表后数据讯速下降到170行。

b、检查JOIN顺序

从上面的SQL可以看到 join的第九张表为
left join f on b.xx = f.xx
f表join的依赖顺序为a,b 只需要a,b表之后即可

那我们通过hint强制指定join顺序,下面写出两种指定顺序的方法,在MySQL8.0版本支持。5.X版本不支持。

#通过JOIN_ORDER指定 select /*+ JOIN_ORDER(a,b,f) */ * from a left join b on a.xx=b.xx left join c on b.xx= c.xx left join d on c.xx = d.xx left join e on d.xx = e.xx left join e1 on d.xx = e1.xx left join e2 on c.xx =e2.xx left join e3 on d.xx = e3.xx left join e4 on c.xx =e4.xx left join f on b.xx = f.xx left join g on f.xx = g.xx where a.col = ? and b.col =? and f.col =? and g.col =? #通过JOIN_FIXED_ORDER指定 select /*+ qb_name(qb1) JOIN_FIXED_ORDER(@qb1) */ * from a left join b on a.xx=b.xx left join f on b.xx = f.xx left join c on b.xx= c.xx left join d on c.xx = d.xx left join e on d.xx = e.xx left join e1 on d.xx = e1.xx left join e2 on c.xx =e2.xx left join e3 on d.xx = e3.xx left join e4 on c.xx =e4.xx left join g on f.xx = g.xx where a.col = ? and b.col =? and f.col =? and g.col =?

再次查看执行计划
c327281ea80340ccb634f3be477b09ac.png
通过执行计划看到在第三张表后。行数就讯速下降到170行了。而因为join顺序依赖关系,f表是不能放在b表前面的。

这里多说一句,为什么明明是left join join后的行数下降了。这是因为虽然写的是left join 只要where 条件上有left join 右表的过滤条件。 MySQL会自动优化为 inner join 这个行为是可观测的,如果不知道怎么观测这个left join 自动转化为inner join的。可以评论区留言,我给大家写个演示。

通过执行计划可以看到最终执行时间为2.67S

这里还有另外一优化手段:将b,f表写到子查询中先过滤,这个优化手法叫延迟关联,在这篇文章中就不展开了。有兴趣的可参见这篇MySQL/SQL Server分页优化(三)

不需要做多大的调整去改写SQL.只需要调整join顺序就有这样的性能提升。

3、多表join该如何确定顺序

a、驱动表的选择

多表join 第一件大事就是确定驱动表,MySQL join 原则:小表驱动大表。所以常规来说一定要小表做驱动表。

select * from a inner join b on a.xx = b.xx where a.xx=? and b.xx=?

原则上就是a与b哪个表小,那个表就会被选为驱动表。 但这里有一个情况会打破这个常规。如果大表的where条件过滤性很好。比如a表数据10W. b表数据1000W 但执行b.xx=?后。b表只会剩下30W。 那这个时候也该选b表做驱动表

b、被驱表的顺序排列

join 关系只会有两种 1:1 和1:N
join顺序就该把1:1的放在前面。这样就不会导致join数据迅速膨胀。然后再按照N的大小来排,N的大小可以通过 show index from tablename的区分度+表行数来估算
区分度大+行数小的也要先join
最后再排区分度小+表行数大的表,这类表往往是导致join结果快速膨胀的凶手。

还有一个特列就是 where条件有很好的过滤性。能大幅过滤行数的也要放在join顺序靠前位置。
本贴的SQL就属于这种情况。

MySQL生产优化合辑

最后修改时间:2025-12-26 09:56:03
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论