MySQL查询优化:索引失效的10个坑,90%的程序员都踩过!

一、开场白:一条慢SQL引发的“血案”

上周半夜被运维同事的电话惊醒:“线上数据库CPU飙到100%,快看看!”
排查发现,一条看似普通的查询:

SELECT * FROM order WHERE user_id = 123 AND create_time > '2023-01-01'

竟然执行了30秒!明明给user_id建了索引,为什么还是全表扫描?

今天就来聊聊MySQL索引那些事儿:为什么精心设计的索引会失效?如何用Explain诊断慢SQL?纯干货,建议收藏!


二、索引失效?先搞懂MySQL怎么“查字典”

索引就像字典的目录,正常情况下MySQL会:

  1. 先查目录(索引)找到对应页码(数据位置)
  2. 再翻到对应页码找到内容(数据)

但如果索引失效,MySQL就会变成“逐字逐句翻字典”,也就是全表扫描,速度慢100倍!


三、索引失效的10大经典场景(附避坑指南)

1. 隐式转换:最容易踩的坑!

-- user_id是varchar类型
SELECT * FROM user WHERE user_id = 123;  -- 失效!
SELECT * FROM user WHERE user_id = '123'; -- 有效

原理:数字和字符串比较会触发类型转换,相当于在索引字段上做运算

2. 使用函数/表达式

SELECT * FROM order WHERE SUBSTR(create_time, 1, 7) = '2023-01'; -- 失效
SELECT * FROM order WHERE create_time >= '2023-01-01' AND create_time < '2023-02-01'; -- 有效

3. LIKE以%开头

SELECT * FROM user WHERE username LIKE '%小明'; -- 失效
SELECT * FROM user WHERE username LIKE '小明%'; -- 有效

4. OR连接非索引字段

SELECT * FROM product WHERE id = 123 OR price = 99; -- price无索引则整体失效

5. 违背最左匹配原则

-- 联合索引(a,b,c)
SELECT * FROM table WHERE b=2 AND c=3; -- 失效
SELECT * FROM table WHERE a=1 AND c=3; -- 只用到a索引
SELECT * FROM table WHERE a=1 AND b=2 AND c=3; -- 全索引生效

6. 使用NOT IN / NOT EXISTS

SELECT * FROM user WHERE id NOT IN (1,2,3); -- 可能失效

建议用LEFT JOIN替代:

SELECT a.* FROM user a LEFT JOIN blacklist b ON a.id = b.id WHERE b.id IS NULL;

7. 索引字段使用IS NULL / IS NOT NULL

SELECT * FROM user WHERE phone IS NULL; -- 可能失效

优化:业务允许的话用默认值代替NULL

8. 全表扫描更快的情况

当查询结果超过30%的数据时,MySQL会认为全表扫描比索引查询更快

9. 使用!=或<>运算符

SELECT * FROM product WHERE price != 100; -- 可能失效

10. 索引字段被更新频繁

写入频繁的字段建索引会导致更新性能下降


四、Explain执行计划:SQL诊断神器

学会看Explain,就能像医生一样诊断SQL问题!

1. 怎么用?

EXPLAIN SELECT * FROM order WHERE user_id = 123;

2. 关键字段解读

字段含义重要值
type访问类型ref(索引查找)、range(范围查找)、ALL(全表扫描)
key使用的索引NULL表示未使用索引
rows预估行数数值越小越好
Extra额外信息Using index(覆盖索引)、Using filesort(文件排序)、Using temporary(临时表)

3. 实战分析:从ALL到ref的优化

优化前

+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | order | ALL  | NULL          | NULL | NULL    | NULL | 100w | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

优化后

+----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | order | ref  | idx_user_id   | idx_user_id | 4       | const | 100  | Using where |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+

五、查询优化最佳实践

1. 索引设计三原则

  • 高频查询字段优先建索引
  • 避免过度索引(影响写入性能)
  • 联合索引遵循最左匹配原则

2. SQL编写规范

  • 避免SELECT *,只查需要的字段
  • 控制JOIN表数量(建议不超过3张)
  • 大表分页用主键自增ID:WHERE id > 1000 LIMIT 100

3. 定期维护

  • 分析慢查询日志(slow_query_log)
  • 定期优化表结构和索引
  • 使用pt-index-usage工具分析索引使用情况

六、总结

索引失效就像“隐形的性能杀手”,稍不注意就会让系统变慢。记住:

  1. 写SQL时多思考:这个查询会走索引吗?
  2. 养成用Explain分析SQL的习惯
  3. 没有万能的优化方案,要结合业务场景

最后送大家一句口诀:
“全值匹配我最爱,最左前缀要遵守;带头大哥不能死,中间兄弟不能断;索引列上不计算,范围之后全失效”


关注服务端技术精选,获取更多后端实战干货!

你遇到过哪些奇葩的SQL性能问题?欢迎在评论区分享你的故事!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱娃哈哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值