上周半夜被运维同事的电话惊醒:“线上数据库CPU飙到100%,快看看!” 排查发现,一条看似普通的查询:
SELECT * FROM order WHERE user_id = 123 AND create_time > '2023-01-01'
竟然执行了30秒!明明给user_id建了索引,为什么还是全表扫描?
今天就来聊聊MySQL索引那些事儿:为什么精心设计的索引会失效?如何用Explain诊断慢SQL?纯干货,建议收藏!
索引就像字典的目录,正常情况下MySQL会:
但如果索引失效,MySQL就会变成“逐字逐句翻字典”,也就是全表扫描,速度慢100倍!
-- user_id是varchar类型
SELECT * FROM user WHERE user_id = 123; -- 失效!
SELECT * FROM user WHERE user_id = '123'; -- 有效
原理:数字和字符串比较会触发类型转换,相当于在索引字段上做运算
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'; -- 有效
SELECT * FROM user WHERE username LIKE '%小明'; -- 失效
SELECT * FROM user WHERE username LIKE '小明%'; -- 有效
SELECT * FROM product WHERE id = 123 OR price = 99; -- price无索引则整体失效
-- 联合索引(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; -- 全索引生效
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;
SELECT * FROM user WHERE phone IS NULL; -- 可能失效
优化:业务允许的话用默认值代替NULL
当查询结果超过30%的数据时,MySQL会认为全表扫描比索引查询更快
SELECT * FROM product WHERE price != 100; -- 可能失效
写入频繁的字段建索引会导致更新性能下降
学会看Explain,就能像医生一样诊断SQL问题!
EXPLAIN SELECT * FROM order WHERE user_id = 123;
ref | ||
Using index |
优化前:
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 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 |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-------------+
WHERE id > 1000 LIMIT 100
索引失效就像“隐形的性能杀手”,稍不注意就会让系统变慢。记住:
最后送大家一句口诀:“全值匹配我最爱,最左前缀要遵守;带头大哥不能死,中间兄弟不能断;索引列上不计算,范围之后全失效”