加了索引以后,想确认数据库到底有没有用到它,可以用 执行计划 (EXPLAIN/EXPLAIN ANALYZE) 来验证。
1. MySQL 的方式
基本用法
EXPLAIN SELECT ...;或者更详细:
EXPLAIN ANALYZE SELECT ...;(MySQL 8.0+ 支持 ANALYZE,会实际执行 SQL 并显示真实耗时和行数,比普通 EXPLAIN 更准确)
重点看几个字段
- type
ALL:全表扫描(索引没用上 🚨)index:全索引扫描(一般也不理想)range/ref/eq_ref:说明用到了索引,越靠近eq_ref越好
- possible\_keys:可能会用到的索引
- key:实际用到的索引(这个最关键)
- rows:预估扫描的行数(越小越好)
- Extra
Using index→ 覆盖索引(最佳情况)Using where→ 索引部分过滤Using filesort→ 说明 ORDER BY 没用上索引,要优化 🚨Using temporary→ 有临时表,通常是 DISTINCT 或 GROUP BY 引起 🚨
2. PostgreSQL 的方式
基本用法
EXPLAIN SELECT ...;更详细:
EXPLAIN (ANALYZE, BUFFERS) SELECT ...;重点看执行计划里的节点
- Seq Scan(全表扫描) → 索引没生效
- Index Scan / Index Only Scan → 用到了索引 ✅
- Bitmap Index Scan → 用到了索引,但结果要回表
- Sort → ORDER BY 需要额外排序,索引没覆盖到 🚨
PostgreSQL 的 ANALYZE 会给出实际耗时、行数对比预估值,如果差异大说明统计信息或索引设计有问题。
3. 实战举例(MySQL)
你这个慢 SQL:
EXPLAIN SELECT DISTINCT r.role_id, r.role_name, ...
FROM sys_role r
LEFT JOIN sys_user_role sur ON sur.role_id = r.role_id
LEFT JOIN sys_user u ON u.user_id = sur.user_id AND u.tenant_id = '1110'
...
WHERE (r.del_flag = '0' AND r.status = '0')
AND r.tenant_id = '1110'
ORDER BY r.role_sort ASC, r.create_time ASC;执行计划里如果看到:
sys_role→key=idx_sys_role_tenant_status_sort✅sys_user_role→key=idx_sys_user_role_roleid✅sys_user→key=idx_sys_user_userid_tenant✅Extra不出现Using filesort✅
那说明索引已经真正生效。
4. 小技巧
- 先跑一次
ANALYZE TABLE(MySQL) 或VACUUM ANALYZE(Postgres),更新统计信息,否则优化器可能选不到索引。 - 如果索引建了但没用,可能原因:
- 索引顺序不对(组合索引里,必须先用到前面的列才会生效)
- 查询条件选择度太低(比如
status='0'这种几乎全表都满足,优化器会直接走全表扫描) - 排序字段没包含在索引里 → 会导致
filesort DISTINCT、OR、函数操作(例如DATE(create_time))可能导致索引失效

















