400-920-5594
首页
解决方案
经典案例
资讯中心
关于我们
首页 > 咨询中心 > 技术分享
MySQL慢查询根治实战:90%项目索引失效、SQL烂写法,彻底解决线上接口卡顿
2026-09-11 9 技术分享

成都软件开发

  后端线上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重构、覆盖索引优化这套完整流程,足以应对企业千万级数据表的所有性能问题,彻底根治线上数据库瓶颈。

推荐文章查看更多》