怎么判断索引被用上
索引建了不等于被用。判定要看三层证据:EXPLAIN 的 key 是否命中、key_len 用到了联合索引的第几列;优化器 trace 说明为什么选它或被否决;长期统计确认整体效果。反过来,长期不用的索引只会拖慢写入、占用空间、干扰优化器选择,删之前用不可见索引做灰度印证。
基本信息
| 适用版本 | MySQL 5.7 / 8.0(sys 视图在 5.7 起自带) |
|---|---|
| 第一层 | EXPLAIN / EXPLAIN FORMAT=JSON 看 key、key_len、rows、Extra |
| 第二层 | optimizer_trace 看候选索引与代价比较 |
| 第三层 | sys.schema_unused_indexes、performance_schema.table_io_waits_summary_by_index_usage |
| 权限要求 | 读 sys 与 performance_schema 需相应 SELECT 权限 |
典型使用场景
- 慢查询定位:确认语句是否按预期走了新加的索引
- 上线前验证:新索引是否被优化器选中,避免白建
- 清理冗余索引:找出长时间零使用的索引,评估后下线
- 联合索引设计:用 key_len 反推现有条件用到了第几列,决定是否调整列序
- 排查计划抖动:同一 SQL 昨天走索引今天不走,用 trace 看代价变化原因
EXPLAIN 与优化器追踪
EXPLAIN 与优化器追踪
-- 第一步:看是否命中索引
EXPLAIN SELECT * FROM appdb.orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 10;
-- 第二步:打开优化器追踪,只看当前会话的这条语句
SET optimizer_trace = 'enabled=on';
SELECT * FROM appdb.orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 10;
SELECT * FROM information_schema.OPTIMIZER_TRACE\G
SET optimizer_trace = 'enabled=off'; # 用完立即关闭长期使用情况盘点
长期使用情况盘点
-- 启动以来一次都没被用过的索引(实例重启后统计归零)
SELECT * FROM sys.schema_unused_indexes;
-- 冗余索引:被其他索引的前缀完全覆盖
SELECT * FROM sys.schema_redundant_indexes;
-- 按索引统计读次数,判断冷热
SELECT object_name, index_name, count_star
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE object_schema = 'appdb' AND object_name = 'orders'
ORDER BY count_star ASC;证据链对照
| 证据 | 能回答什么 | 局限 |
|---|---|---|
| EXPLAIN key | 本次执行是否用索引、用了哪一个 | 不解释为什么不用别的索引 |
| key_len | 联合索引实际用到第几列 | 不区分查找还是排序 |
| rows / filtered | 优化器估算的扫描量与过滤率 | 统计信息陈旧时会严重失真 |
| optimizer_trace | 候选索引与代价比较的完整过程 | 输出很大,只适合短时定位 |
| schema_unused_indexes | 长期是否被使用 | 基于启动以来统计,重启即归零 |
OPTIMIZER_TRACE 输出包含每个候选索引的代价估算,体积很大,只针对单条语句短时开启;会话用完必须关闭,长连接上忘记关闭会持续累积。
用 USE INDEX 强制索引只是应急手段:优化器不选某个索引往往是数据分布或统计信息变了,先 ANALYZE TABLE 或调整索引设计,长期靠强制只会掩盖根因并把风险留给下一次数据变化。