在 MySQL 中,索引并不一定总是有效。虽然索引可以显著加速查询,但在某些场景下,索引可能失效或未被优化器选中。以下是详细分析和排查方法:
一、索引不一定有效的常见原因
1. 查询条件导致索引失效
- 对索引列使用函数或计算
例如:SELECT * FROM users WHERE YEAR(create_time) = 2023;
原因:索引存储的是原始值,函数操作会破坏索引的顺序性。
优化:改写为范围查询:SELECT * FROM users WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';
- 隐式类型转换
例如:SELECT * FROM users WHERE age = '25';
(age
是INT
类型)
原因:MySQL 会将字符串'25'
转换为数字,导致索引失效。
优化:确保查询值与字段类型一致:SELECT * FROM users WHERE age = 25;
LIKE
以通配符开头
例如:SELECT * FROM users WHERE name LIKE '%Alice';
原因:通配符%
在左侧会导致无法利用索引的排序特性。
优化:改写为右侧通配符:SELECT * FROM users WHERE name LIKE 'Alice%';
OR
连接非索引列
例如:SELECT * FROM users WHERE name = 'Alice' OR age = 25;
原因:如果age
未被索引,优化器可能放弃使用索引。
优化:拆分为两个独立查询并使用UNION
:SELECT * FROM users WHERE name = 'Alice' UNION SELECT * FROM users WHERE age = 25;
2. 索引设计不合理
- 低选择性列
例如:gender
字段(仅Male
/Female
两种值)。
原因:索引的选择性低,全表扫描可能更快。
优化:避免为低选择性列建索引。 - 联合索引未遵循最左前缀原则
例如:联合索引(a, b, c)
,查询条件为WHERE b = 2
。
原因:未使用最左列a
,索引失效。
优化:调整查询条件或索引顺序。 - 范围查询后的字段失效
例如:联合索引(a, b, c)
,查询条件为WHERE a = 1 AND b > 10
。
原因:范围查询b > 10
后的字段c
无法使用索引。
优化:将范围查询字段放在联合索引的末尾。
3. 数据分布与查询成本
- 小表全表扫描更快
例如:表仅有几十行数据,SELECT * FROM users;
原因:索引查找 + 回表的成本高于直接全表扫描。
优化:无需为小表创建索引。 - 统计信息不准确
原因:MySQL 优化器基于统计信息估算成本,若统计信息未更新,可能导致错误决策。
优化:定期执行ANALYZE TABLE
更新统计信息。
二、如何排查索引效果?
1. 使用 EXPLAIN
分析执行计划
EXPLAIN SELECT * FROM users WHERE name = 'Alice';
关键字段解析:
type
:查询类型(从优到劣:const
>ref
>range
>index
>ALL
)。const/ref
:使用了索引。ALL
:全表扫描,索引未生效。
key
:实际使用的索引名称。- 若为
NULL
,表示未使用索引。
- 若为
rows
:预估扫描行数,值越小越好。Extra
:额外信息。Using index
:覆盖索引(无需回表)。Using filesort
:需要额外排序,可能需优化索引。Using temporary
:需要临时表,可能需优化索引。
2. 监控系统状态变量
SHOW STATUS LIKE 'Handler_read%';
Handler_read_key
:通过索引读取的请求数。Handler_read_rnd_next
:全表扫描的请求数。- 若
Handler_read_key
远小于Handler_read_rnd_next
,说明索引使用率低。
- 若
3. 检查索引使用情况
SHOW INDEX FROM table_name;
- 查看索引的使用频率和是否被优化器选择。
4. 分析慢查询日志
- 启用慢查询日志,定位未使用索引的 SQL:
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; -- 超过 1 秒的查询记录
三、优化索引效果的建议
- 覆盖索引
- 查询字段全部包含在索引中,避免回表。
例如:为 SELECT name FROM users WHERE age > 30
创建索引 (age, name)
。
- 遵循最左前缀原则
- 联合索引的最左列必须出现在查询条件中。
- 定期清理冗余索引
- 删除未使用的索引(通过
SHOW INDEX
和EXPLAIN
分析)。
- 删除未使用的索引(通过
- 更新统计信息
- 执行
ANALYZE TABLE
确保优化器决策准确。
- 执行
- 避免过度索引
- 每个索引增加写操作成本,权衡查询与写入性能。
四、示例:索引失效与优化
失效场景
-- 索引失效:使用函数
EXPLAIN SELECT * FROM orders WHERE YEAR(order_date) = 2023;
-- type=ALL,key=NULL
优化后
-- 改写为范围查询
EXPLAIN SELECT * FROM orders
WHERE order_date >= '2023-01-01' AND order_date < '2024-01-01';
-- type=range,key=index_name
五、总结
索引失效原因 | 排查方法 | 优化建议 |
---|---|---|
函数/计算操作 | EXPLAIN 查看 key 是否为 NULL | 避免对索引列进行操作 |
隐式类型转换 | 检查查询值与字段类型 | 确保类型一致 |
LIKE 通配符开头 | EXPLAIN 查看 type | 改为右侧通配符 |
联合索引未遵循最左前缀 | EXPLAIN 查看 key | 调整索引顺序或查询条件 |
小表全表扫描 | EXPLAIN 查看 rows | 不为小表创建索引 |
最终结论:索引是否生效取决于查询条件、索引设计和数据分布。通过 EXPLAIN
和监控工具分析索引效果,并针对性优化,才能充分发挥索引的价值。
THE END