数据库慢查询定位与优化一线踩坑实录
上周三凌晨两点被运营的电话炸醒。说大促的优惠券领取接口崩了,用户刷三分钟都领不到,客服的后台已经被骂爆了。我套上拖鞋开电脑,第一反应就是去看数据库的慢日志。
MySQL慢查询日志查询结果截图
我把执行计划拉出来的瞬间,人都麻了。type列直接是ALL,意味着走了全表扫描,预估扫描的行数整整120万。每次用户点一次领取按钮,数据库就得把这张半年没清理的优惠券表从头到尾扫一遍。
合着之前没崩,全靠用户量小撑着。

MySQL联合索引最左匹配执行计划对比图
这就踩了联合索引最左匹配原则的坑。联合索引是按从左到右的顺序排序的,一旦遇到范围查询,后面的列索引就直接失效。你把create_time放在最前面,后面的user_id根本用不上索引的排序能力。
我当场把那个没用的单独索引、顺序建反的联合索引全删了,重新建了个(user_id, create_time)的联合索引。重新跑执行计划的时候,预估扫描行数直接从120万掉到了87行。SQL执行耗时从2.3秒降到了3毫秒。
差了快一千倍啊。
别上来就加索引先看日志
好多新人碰到慢查询的第一反应,就是找DBA要权限加索引。 根本不看这条SQL到底慢在哪。 上次那个把接口拖垮的SQL,本质上就是查用户名下的待领取优惠券,where条件带了user_id的等值匹配,还有create_time在近7天的范围过滤。之前上线的时候这张表才几千行,DBA随手给加了个单独的create_time索引,想着能撑住就行。你以为的索引可能根本没生效
这时候有人会说,那不是加了create_time的索引吗,咋会全表扫? 问得好。 之前那个单独的create_time索引,确实能用到,但效率低到离谱。因为查询条件里user_id的过滤性更强——单个用户在7天内的优惠券可能就两三条,但整个create_time近7天的记录可能有几十万条。更坑的是,之前有人试过把联合索引建反,搞了个(create_time, user_id)的索引。