后端线上80%的接口卡顿、超时、服务CPU打满问题,根本不是代码问题,是SQL写得烂、索引用错、隐形失效导致的。
很多开发者习惯性建索引就万事大吉,结果生产环境高并发、大数据量下,索引直接失效、全表扫描横行,单条SQL执行几秒甚至几十秒,直接拖垮整个服务。
更头疼的是:本地测试数据量小,SQL再烂也秒查;一旦上线千万级数据表,所有隐性问题全部爆发。
今天我们摒弃网上碎片化、老旧的优化口诀,结合线上真实故障,拆解慢查询排查链路、10大索引失效场景、高危SQL重构方案、生产规范,零基础也能快速搞定数据库性能优化。
一、先搞懂:如何精准抓取线上慢查询?
优化的前提是找到问题SQL,不要凭感觉优化。MySQL自带慢查询日志,可精准定位所有拖垮性能的语句。
1、开启慢查询日志(生产通用配置)
默认慢日志是关闭状态,需要手动开启,设置阈值:执行超过1秒的SQL全部记录。
# 查看慢日志状态 show variables like '%slow_query%'; # 开启慢查询日志 set global slow_query_log = ON; # 慢查询阈值:超过1秒即记录 set global long_query_time = 1; # 记录未使用索引的SQL set global log_queries_not_using_indexes = ON;2、EXPLAIN 分析SQL执行计划
抓到慢SQL后,第一步不是改代码,而是用 EXPLAIN 看执行计划,精准定位问题:是否走索引、是否全表扫描、索引精度如何。
核心关键字段解读(生产最关键):
type:执行级别,优先级:system > const > eq_ref > ref > range > index > ALL
ALL:全表扫描,性能最差,必须优化
key:实际命中的索引,NULL代表索引失效
rows:扫描行数,数值越大越危险
Extra:Using filesort(文件排序)、Using temporary(临时表)都是高危信号
二、生产最高频:10大索引失效真实场景
绝大多数人索引失效,不是没建索引,而是写法不规范导致索引隐形失效,这是线上慢查询的重灾区。
1、索引列使用函数运算(百分百失效)
错误原因:数据库无法使用函数计算后的索引,强制全表扫描。
-- 错误:索引字段做函数运算 SELECT * FROM user WHERE DATE(create_time) = '2026-09-11'; -- 正确:区间匹配,命中索引 SELECT * FROM user WHERE create_time >= '2026-09-11 00:00:00' AND create_time <= '2026-09-11 23:59:59';2、隐式类型转换导致索引失效
错误原因:字段是字符串类型,查询用数字,MySQL会自动隐式转换,索引失效。
-- 错误:phone为varchar类型,传入数字 SELECT * FROM user WHERE phone = 13800138000; -- 正确:类型严格匹配 SELECT * FROM user WHERE phone = '13800138000';3、索引列使用 != <> 不等号
原理:不等号筛选数据离散度高,MySQL优化器直接放弃索引,走全表扫描。
-- 错误:不等号导致索引失效 SELECT * FROM order WHERE status != 0; -- 优化思路:业务改写,用精准范围替代不等号 SELECT * FROM order WHERE status IN(1,2,3);4、like 左模糊、全模糊查询
规则:右模糊走索引,左模糊、全模糊直接失效。
-- 错误:左模糊/全模糊,索引失效 SELECT * FROM user WHERE name LIKE '%张三%'; SELECT * FROM user WHERE name LIKE '%张三'; -- 正确:右模糊命中索引 SELECT * FROM user WHERE name LIKE '张三%';5、or 连接无索引字段
坑点:or 左右字段必须都有索引,只要一个无索引,整体索引失效。
-- 错误:phone有索引,address无索引,整体失效 SELECT * FROM user WHERE phone = '13800' OR address = '北京'; -- 优化:拆分语句、union 合并 SELECT * FROM user WHERE phone = '13800' UNION SELECT * FROM user WHERE address = '北京';6、联合索引不遵循最左匹配原则
核心规则:联合索引(a,b,c),必须先匹配a,跳过a直接查b/c,索引完全失效。
-- 联合索引:idx_user(a,b,c) -- 失效:跳过最左a字段 SELECT * FROM user WHERE b = 1 AND c = 2; -- 生效:遵循最左匹配 SELECT * FROM user WHERE a = 1 AND c = 2;7、not in / not exists 高危写法
大数据量表下,not in 直接放弃索引,全表扫描,性能极差,严禁用于生产。
8、order by、group by 字段无索引
排序、分组字段无索引,会出现 Using filesort、Using temporary,产生临时表、文件排序,百万级数据直接卡死。
9、参数字段为空导致索引失效
业务动态查询中,传入空字符串、null,会导致索引匹配失效,触发全表扫描。
10、select * 滥用导致索引覆盖失效
查询所有字段,无法触发覆盖索引,必须回表查询,大幅降低性能。
三、生产高频慢SQL重构实战
1、分页越查越慢问题
烂写法:offset 超大偏移量,数据库需要遍历前面所有数据,越往后越慢。
-- 烂写法:偏移量过大,超慢 SELECT * FROM order ORDER BY id DESC LIMIT 10000,10; -- 优化写法:主键精准定位分页 SELECT * FROM order WHERE id < 10000 ORDER BY id DESC LIMIT 10;2、批量in查询优化
in数据量过大时,拆分批次查询,避免单次检索数据过多导致数据库卡死。
3、避免重复统计count(*)
count(*)、count(1) 无差别,但禁止多条件重复count,可使用子查询一次性统计。
四、覆盖索引:提升性能10倍的终极方案
原理:索引字段包含查询所需的全部字段,无需回表查询,直接从索引树拿数据,性能拉满。
场景:高频查询、列表分页、统计接口优先使用覆盖索引。
-- 普通索引:需要回表 CREATE INDEX idx_phone ON user(phone); -- 覆盖索引:直接命中,无需回表 CREATE INDEX idx_phone_cover ON user(phone,name,age);EXPLAIN 中 Extra 出现Using index即代表覆盖索引生效。
五、生产环境MySQL性能强制规范
1、禁止使用 select *,按需查询字段,优先使用覆盖索引;
2、禁止索引字段做函数运算、隐式转换、左右模糊匹配;
3、联合索引严格遵循最左匹配原则,高频字段放左侧;
4、超大分页禁止 offset 偏移查询,使用主键分页替代;
5、严禁 in、not in 超大集合查询,必须拆分批次;
6、排序、分组字段必须建立索引,杜绝文件排序和临时表;
7、定期清理无用索引、重复索引,减少写入开销。
六、总结
MySQL慢查询优化,从来不是玄学,而是避开失效场景、规范SQL写法、合理设计索引。
线上绝大多数数据库卡顿、CPU爆满、接口超时,都是开发者不注意细节、滥用烂SQL、建无效索引导致的人为性能事故。
掌握慢日志排查、EXPLAIN分析、索引避坑、SQL重构、覆盖索引优化这套完整流程,足以应对企业千万级数据表的所有性能问题,彻底根治线上数据库瓶颈。