目录
1. 使用 EXPLAIN 分析执行计划详解Mysql的Explain语句
一、快速定位索引失效的步骤
1. 使用 EXPLAIN
分析执行计划详解Mysql的Explain语句
EXPLAIN SELECT * FROM users WHERE age = 25 AND name = 'John';
重点关注以下字段:
type:若显示
ALL
(全表扫描)或index
(全索引扫描),可能索引未生效。key:实际使用的索引名称,若为
NULL
表示未使用索引。rows:预估扫描的行数,数值过大说明索引可能未生效。
Extra:若出现
Using filesort
或Using temporary
,可能索引未被用于排序或分组。
2. 确认索引是否存在
SHOW INDEX FROM users; -- 查看表的索引信息
确保查询涉及的列(尤其是 WHERE
、JOIN
、ORDER BY
中的列)已创建索引。
3. 检查查询条件是否符合索引规则
最左前缀原则:联合索引
(a, b, c)
必须按顺序使用,跳过中间列会导致后续列无法使用索引。WHERE a=1 AND c=3; -- 仅使用到 a 列的索引,c 列无法生效
范围查询阻断索引:范围查询(
>
、<
、BETWEEN
)后的列无法使用索引。WHERE a>10 AND b=20; -- 仅 a 列使用索引,b 列无法生效
二、常见索引失效场景及解决方法
1. 索引列参与计算或函数
失效示例:
SELECT * FROM users WHERE YEAR(create_time) = 2023; -- 对索引列使用函数 SELECT * FROM users WHERE age + 10 > 30; -- 对索引列进行运算
优化方法:改写为直接使用索引列。
SELECT * FROM users WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';
2. 隐式类型转换
失效示例:
-- 假设 phone 是 VARCHAR 类型,但传入数字 SELECT * FROM users WHERE phone = 13800138000; -- 字符串转数字导致索引失效
优化方法:确保数据类型一致。
SELECT * FROM users WHERE phone = '13800138000';
3. 使用 LIKE
以通配符开头
失效示例:
SELECT * FROM users WHERE name LIKE '%John%'; -- 前导通配符导致索引失效
优化方法:尽量使用右通配符。
SELECT * FROM users WHERE name LIKE 'John%'; -- 可能使用索引
4. 使用 OR
连接非索引列
失效示例:
-- 假设 age 有索引,address 无索引 SELECT * FROM users WHERE age = 25 OR address = 'Beijing'; -- 全表扫描
优化方法:改用
UNION
拆分查询。SELECT * FROM users WHERE age = 25 UNION SELECT * FROM users WHERE address = 'Beijing';
5. 索引选择性过低
问题现象:索引列的值重复率过高(如性别字段),MySQL 可能放弃使用索引。
优化方法:删除低选择性索引,或结合其他列创建联合索引。
ALTER TABLE users ADD INDEX idx_gender_age (gender, age);
6. 联合索引顺序错误
问题现象:联合索引
(a, b, c)
,但查询未按最左前缀顺序使用,导致索引部分失效。失效示例:
-- 索引 (a, b, c) SELECT * FROM table WHERE b = 2 AND a = 1; -- 正常使用索引(优化器自动调整顺序) SELECT * FROM table WHERE a = 1 AND c = 3; -- 仅用到 a 列索引,c 未生效 SELECT * FROM table WHERE b = 2; -- 索引完全失效(未使用最左列 a)
优化方法:
调整查询条件顺序,确保按最左前缀匹配。
根据高频查询场景,设计合理的联合索引顺序。
7. 使用 !=
或 <>
操作符
问题现象:非等值查询(如
!=
、NOT IN
)可能导致索引失效。失效示例:
SELECT * FROM users WHERE age != 25; -- 可能全表扫描 SELECT * FROM orders WHERE status NOT IN (1, 2);
优化方法:
改写为等值查询或范围查询:
SELECT * FROM users WHERE age < 25 OR age > 25; -- 仍可能失效,需结合其他条件
若数据分布倾斜,强制使用索引(需测试验证):
SELECT * FROM users FORCE INDEX(idx_age) WHERE age != 25;
8. IS NULL
或 IS NOT NULL
条件
问题现象:索引列上使用
IS NULL
或IS NOT NULL
可能导致索引失效。失效示例:
SELECT * FROM users WHERE phone IS NULL; -- 可能全表扫描
优化方法:若 NULL 值较少,添加条件冗余字段:
ALTER TABLE users ADD COLUMN is_phone_null TINYINT(1) DEFAULT 0; CREATE INDEX idx_phone_null ON users(is_phone_null); SELECT * FROM users WHERE is_phone_null = 1;
9. 多个索引ORDER BY
顺序不一致
问题现象:排序字段顺序与索引顺序不匹配,导致无法利用索引排序。
失效示例:
-- 索引 (a, b) SELECT * FROM table WHER ORDER BY a ASC, b DESC; -- 2个索引顺序不一致导致失效
优化方法:调整联合索引顺序,同升同降。
SELECT * FROM table WHER ORDER BY a ASC, b ASC; 同升同降
10. 全文索引的误用
问题现象:错误使用
LIKE
或MATCH AGAINST
导致索引失效。失效示例:
-- 未使用全文索引 SELECT * FROM articles WHERE content LIKE '%database%';
优化方法:
对文本搜索需求改用全文索引(FULLTEXT Index)。
避免在全文索引列上混合使用
LIKE
和MATCH
。SELECT * FROM articles WHERE MATCH(content) AGAINST('database');
三、高级排查工具
1. 开启慢查询日志
-- 配置 my.cnf
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2 -- 记录执行超过2秒的SQL
通过慢日志定位高频低效 SQL。
2. 使用 OPTIMIZER_TRACE
分析优化器决策
SET optimizer_trace = 'enabled=on';
SELECT * FROM users WHERE age = 25; -- 执行你的查询
SELECT * FROM information_schema.optimizer_trace; -- 查看优化器选择索引的过程
3. 强制使用索引测试
SELECT * FROM users FORCE INDEX (idx_age) WHERE age = 25; -- 强制使用索引
对比强制索引前后的执行时间,判断优化器是否选错索引。
四、总结
核心原则:索引失效的本质是 无法快速定位数据范围。
关键检查点:
避免对索引列进行计算或函数操作。
确保查询条件符合最左前缀原则。
注意隐式类型转换和通配符使用。
工具辅助:
EXPLAIN
、慢查询日志、OPTIMIZER_TRACE
是排查利器。