加了索引以后,想确认数据库到底有没有用到它,可以用 执行计划 (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_rolekey=idx_sys_role_tenant_status_sort
  • sys_user_rolekey=idx_sys_user_role_roleid
  • sys_userkey=idx_sys_user_userid_tenant
  • Extra 不出现 Using filesort

那说明索引已经真正生效。


4. 小技巧

  • 先跑一次 ANALYZE TABLE (MySQL) 或 VACUUM ANALYZE (Postgres),更新统计信息,否则优化器可能选不到索引。
  • 如果索引建了但没用,可能原因:
  1. 索引顺序不对(组合索引里,必须先用到前面的列才会生效)
  2. 查询条件选择度太低(比如 status='0' 这种几乎全表都满足,优化器会直接走全表扫描)
  3. 排序字段没包含在索引里 → 会导致 filesort
  4. DISTINCTOR函数操作(例如 DATE(create_time))可能导致索引失效

mysql怎么知道索引是否生效_PostgreSQL