确认sql是否命中索引需看explain中type(ref/range/const为优)、rows(越接近返回行数越好)、extra(避免using filesort/temporary);mysql 8.0+推荐format=json,postgresql用explain (analyze, buffers)。

如何快速确认某个 SQL 查询是否命中索引
看执行计划是最直接的办法,但别只扫一眼 EXPLAIN 输出里有没有 key 字段——很多同学以为出现索引名就等于“走索引”,其实未必。
关键要看三处:type(最好是 ref、range、const;ALL 就是全表扫描)、rows(预估扫描行数,远大于实际返回行数往往说明索引效率低)、Extra(出现 Using filesort 或 Using temporary 通常意味着索引没覆盖排序或分组)。
- MySQL 8.0+ 建议用
EXPLAIN FORMAT=JSON,比传统格式更清楚展示索引选择逻辑 - PostgreSQL 用
EXPLAIN (ANALYZE, BUFFERS),真实执行并反馈 I/O 开销 - 避免在测试时用
LIMIT 10掩盖问题:优化器可能因数据量小跳过索引,加SQL_NO_CACHE(MySQL)或清缓存再测
复合索引字段顺序为什么不能随便调换
索引是按定义顺序构建的 B+ 树,等值查询(=)可以跳过前导列,但范围查询(>、BETWEEN)之后的列就失效了。
比如索引 (a, b, c):
-
WHERE a = 1 AND b > 10→ 可用a和b,c无法过滤 -
WHERE b = 5→ 完全无法使用该索引(缺少前导列a) -
WHERE a = 1 AND c = 3→ 只能利用a,c被跳过(中间断层)
排序场景更敏感:ORDER BY a, b 能用 (a,b) 索引,但 ORDER BY b, a 就不行——除非改写为 ORDER BY b ASC, a ASC 且索引是 (b,a)。
哪些操作会让索引“静默失效”
不是报错才叫失效,很多情况下索引还在,但优化器主动弃用,你却毫无察觉。
- 对索引列做函数操作:
WHERE YEAR(created_at) = 2023→ 改成WHERE created_at >= '2023-01-01' AND created_at - 隐式类型转换:
WHERE user_id = '123'(user_id是INT)→ 字符串转数字可能触发全表扫描,尤其在大表上 - LIKE 左模糊:
WHERE name LIKE '%abc'→ 无法用索引;LIKE 'abc%'可以 - OR 连接不同列:
WHERE a = 1 OR b = 2→ 若无复合索引覆盖两者,大概率退化为全表扫描
监控索引使用率的实用方法
别只盯着慢查询日志,它漏掉大量“不慢但高频低效”的查询。真正要盯的是索引被选中但未被命中的比例。
MySQL 中查 sys.schema_unused_indexes 视图(需启用 performance_schema),但注意:它只统计“从未被用过”的索引,对“偶尔用、多数时候失效”的情况无感。
- 更准的做法是开启
performance_schema的events_statements_history_long,配合脚本定期提取EXPLAIN结果里的key和rows - PostgreSQL 用
pg_stat_all_indexes查idx_scan(扫描次数)和idx_tup_read(读取行数),比idx_tup_fetch更能反映实际过滤效率 - 警惕“伪高使用率”:一个索引被
UPDATE频繁更新,idx_scan很高,但它可能只是维护开销大,并未帮上查询
索引不是越多越好,每多一个,INSERT/UPDATE/DELETE 就多一份 B+ 树分裂和写放大。最常被忽略的是:删除无用索引后,务必检查应用层是否依赖了某些“恰好生效”的隐式排序或唯一性约束。










