数据库慢查询定位与优化 踩过的那些实战坑
2026-10-28 14:25:16
作者:
fawen
上周三凌晨两点,我把脸埋在枕头里接起运维电话的时候,第一反应是想把手机扔去楼下的景观河。
大促预热的商品列表页转了三圈都刷不出来,客服群已经炸了好几百条消息,查监控的时候才发现,订单库的一条关联查询,单条执行耗时12.7秒,短短三分钟就把应用的数据库连接池占了八成,后续请求全在排队等连接。
真的亏。这种平时跑起来悄无声息的慢查询,从来不会提前给你打招呼。
别上来就加索引,先搞清楚慢在哪
我见过太多人拿到慢查询的第一反应,是打开explain对着执行计划一顿分析,找没走索引的字段,噼里啪啦加一堆索引就完事。
上次团队招的实习生,碰到一条慢查询上来就给订单表加了三个联合索引,结果索引创建跑了半小时,把主库的写入锁了,差点把当天的订单支付流程搞挂。
说白了,你连慢查询的触发阈值是多少、是不是真的因为SQL本身拖慢了执行,都没搞清楚,加索引完全是瞎碰运气。
现在我要求团队把慢查询的捕获阈值直接设成200毫秒,不管业务侧有没有感知,所有超过这个时间的语句全要捞出来归档。
MySQL慢查询日志参数配置界面截图
别嫌麻烦。之前我们从归档日志里捞出来一条平时稳定跑在180毫秒的商家统计接口,大促当天因为缓存配置出错,每秒打过来几百次请求,直接把从库的CPU干到92%,要是等用户投诉了再排查,损失早就兜不住了。
哦对,别总觉得慢查询就是要等用户感知到卡顿了再处理。那些藏在日志里的小毛病,攒到流量峰值的时候,全是能炸穿机房的雷。
很多所谓的慢,根本不是SQL写得烂
说出来你可能不信,上次那条跑了12秒的订单查询,本身写的真的不算差。
explain拉出来看,走了订单表的主键索引,单表扫描行数也就2万行,按照正常的执行速度,最多200毫秒就能跑完。
查了半天才发现,这条查询关联的订单状态字典表,上个月被业务同学加了个隐藏触发器,每次对字典表做查询操作的时候,触发器会自动同步更新一个冗余的商家统计字段,而这个触发器的内部逻辑,根本没加对应的索引,每次触发都要全表扫一遍几十万行的订单明细表。
太多人排查慢查询的时候,眼睛只盯着SQL本身的执行计划,完全忽略了触发器、存储过程、锁等待这些藏在SQL背后的暗坑。
MySQL锁等待排查过程监控截图
还有那种更常见的坑:为了图方便,在查询语句里套三四层嵌套子查询,每个子查询单独跑都快的飞起,但是MySQL优化器会把这些嵌套子查询自动转成多层关联嵌套,一旦扫描行数上去,内存里的临时表直接落盘,跑个几十秒都是常事。
之前运营团队提的月度用户消费统计需求,最初的版本就是三层嵌套子查询,单跑一次要40多秒。后来我们把嵌套逻辑拆成先查最小粒度的用户消费明细,在内存里做分组聚合,整个流程跑下来不到3秒,连个索引都没加。
优化完别直接上生产,留个兜底的余量
优化完别直接上生产,留个兜底的余量
很多人优化完SQL,本地测一下执行时间从10秒降到100毫秒,就开开心心提上线了。
结果大促当天流量翻了五倍,这条之前跑100毫秒的查询,因为数据库CPU被占满,直接飙到8秒,又把连接池给占满了。
现在我们对所有慢查询优化的上线要求是,必须在测试环境压测到日常流量的5倍,观察执行时间的波动范围,而且必须给所有查询加硬超时——超过500毫秒直接中断请求,返回兜底的缓存数据。
别跟我说什么业务数据要绝对准确。真到了数据库被打挂、全页面都刷不出来的时候,那才是最大的不准确。
还有那些动不动就写select *的查询,真的别再用了。上次我们排查一个用户信息接口,发现开发图省事直接select *查了用户表的27个字段,实际前端页面只用了3个。把冗余字段去掉之后,查询的网络传输时间直接降了60%,连SQL逻辑都没改。
说实话,我见过太多团队把慢查询优化当成DBA一个人的事,开发写完SQL就往生产发,出了问题全靠运维和DBA背锅。
当然也不是所有慢查询都要硬优化到毫秒级。那种一天跑一次的月度报表,跑个十几秒根本不影响业务,没必要为了这点性能把代码改的面目全非,浪费开发的时间。
上次那个因为加索引锁了主库的实习生,现在每次写SQL之前,都会先把执行计划跑三遍,看看有没有可能拖慢库的风险。
挺好,都是踩坑踩出来的经验。
免责声明:市场有风险,选择需谨慎!此文仅供参考,不作买卖依据。如有侵权请联系删除。
文章名称:
数据库慢查询定位与优化 踩过的那些实战坑
文章链接:https://m.itcdj.com/it/213.html