数据库慢查询定位与优化一线踩坑实录
2026-10-26 09:34:05
作者:
fawen
上周三凌晨两点被运营的电话炸醒。说大促的优惠券领取接口崩了,用户刷三分钟都领不到,客服的后台已经被骂爆了。我套上拖鞋开电脑,第一反应就是去看数据库的慢日志。
别上来就加索引先看日志
好多新人碰到慢查询的第一反应,就是找DBA要权限加索引。
根本不看这条SQL到底慢在哪。
上次那个把接口拖垮的SQL,本质上就是查用户名下的待领取优惠券,where条件带了user_id的等值匹配,还有create_time在近7天的范围过滤。之前上线的时候这张表才几千行,DBA随手给加了个单独的create_time索引,想着能撑住就行。
MySQL慢查询日志查询结果截图
我把执行计划拉出来的瞬间,人都麻了。type列直接是ALL,意味着走了全表扫描,预估扫描的行数整整120万。每次用户点一次领取按钮,数据库就得把这张半年没清理的优惠券表从头到尾扫一遍。
合着之前没崩,全靠用户量小撑着。
你以为的索引可能根本没生效
这时候有人会说,那不是加了create_time的索引吗,咋会全表扫?
问得好。
之前那个单独的create_time索引,确实能用到,但效率低到离谱。因为查询条件里user_id的过滤性更强——单个用户在7天内的优惠券可能就两三条,但整个create_time近7天的记录可能有几十万条。更坑的是,之前有人试过把联合索引建反,搞了个(create_time, user_id)的索引。
MySQL联合索引最左匹配执行计划对比图
这就踩了联合索引最左匹配原则的坑。联合索引是按从左到右的顺序排序的,一旦遇到范围查询,后面的列索引就直接失效。你把create_time放在最前面,后面的user_id根本用不上索引的排序能力。
我当场把那个没用的单独索引、顺序建反的联合索引全删了,重新建了个(user_id, create_time)的联合索引。重新跑执行计划的时候,预估扫描行数直接从120万掉到了87行。SQL执行耗时从2.3秒降到了3毫秒。
差了快一千倍啊。
别把所有问题都甩给数据库
排查到这里,其实还没结束。
我顺着代码往上翻,又发现个更离谱的问题。开发为了给用户展示优惠券对应的品牌信息,写了个循环,每查到一条优惠券记录,就去查一次品牌表拿品牌名。一共3条优惠券,就发了3次单独的查询请求。
这就是典型的循环嵌套查询。
说真的,这种代码在数据量小的时候根本看不出问题。但高并发场景下,哪怕每个用户多打两次数据库请求,整体的数据库连接数直接就被占满了。
我当时把那段循环改成了把所有优惠券对应的品牌id收集起来,一次批量查完品牌表,再在内存里做映射。整个接口的响应时间又降了40%。
哦对,也不是说所有慢查询都要死磕到极致。比如那种凌晨跑一次的月度报表,慢个十几秒根本没人在意。之前团队有个学弟,熬了两个通宵把一个没人看的报表SQL从15秒优化到2秒,最后发现业务方早就换了新的报表系统。
真的。
太亏了。
日常排查慢查询真的不用等出了线上故障才临时抱佛脚。把慢查询日志的阈值设成200毫秒,每周抽个半小时过一遍攒下来的SQL,把那些隐性能耗高的查询提前优化掉。别等大促流量冲上来的时候,才发现自己写的SQL一直在裸奔。
免责声明:市场有风险,选择需谨慎!此文仅供参考,不作买卖依据。如有侵权请联系删除。
文章名称:
数据库慢查询定位与优化一线踩坑实录
文章链接:https://www.itcdj.com/it/133.html