技术博客

MySQL慢查询从3秒到50毫秒:一次索引优化全过程

技术博客
MySQL慢查询从3秒到50毫秒:一次索引优化全过程

周五下午三点,运维群里弹出告警:某个列表查询接口P99响应时间突破3秒。打开监控面板一看,数据库CPU占用率已经飙到80%。

定位问题SQL

开启慢查询日志后抓到了罪魁祸首——一条关联了三张表的查询语句,WHERE条件里有状态筛选、时间范围和模糊搜索,ORDER BY按创建时间倒序,LIMIT 20。单独执行这条SQL,耗时稳定在3.2秒左右。

EXPLAIN告诉了我什么

跑了一下EXPLAIN,type列显示ALL,rows预估扫描了47万行,Extra里出现了Using filesort和Using temporary。翻译成人话:全表扫描,没命中任何索引,还额外做了排序和临时表操作。

原表上其实有索引,但只有一个单列索引建在status字段上。问题在于查询条件是status + created_at范围 + keyword模糊匹配的组合,单列索引在这种场景下几乎帮不上忙。

联合索引设计思路

联合索引的字段顺序很关键,遵循的原则是:等值查询的字段放前面,范围查询的字段放后面,排序字段尽量包含在索引里避免filesort。按这个逻辑,我建了一个联合索引:(status, created_at)。为什么没把keyword相关字段加进去?因为LIKE '%关键词%'这种左模糊是走不了B+树索引的,加了也白加。

处理模糊搜索

左模糊搜索是索引杀手,但业务上又必须支持。短期方案是把LIKE '%keyword%'改成先通过索引筛选status和时间范围缩小数据集,再在小结果集上做模糊匹配。长期方案是上Elasticsearch做全文检索,把模糊搜索的压力从MySQL转移出去。

优化后的效果

加完联合索引后再跑EXPLAIN:type变成了range,rows从47万降到了2100,Extra里的filesort也消失了。实际执行时间:48毫秒。从3秒到50毫秒以内,差距是60倍。

这次踩坑的几个教训

第一,建表的时候就该根据查询场景设计索引,而不是等到出问题再补。第二,联合索引不是字段越多越好,要看实际查询条件的组合和顺序。第三,EXPLAIN是排查慢查询的第一工具,type列看是否全表扫描,rows看扫描行数,Extra看有没有filesort和temporary。第四,超过百万行的表做LIKE左模糊就别指望MySQL了,该引入搜索引擎就引入。

另外一个容易忽略的点:索引不是免费的。每加一个索引,INSERT和UPDATE都会变慢,因为要同时维护索引树。对写入密集的表,索引数量需要克制,通常控制在5-6个以内。

聊聊你的项目

有架构或成本优化的烦恼?

把你的业务场景告诉我们,专家会给出一份务实的改造与降本建议。


电话咨询 微信咨询 在线咨询 返回顶部
xycx202108

微信扫码咨询

×