EXPLAIN ANALYZE 耗时不准因受缓存、锁和系统负载干扰,应取后两次 Execution Time 平均值,并关注 Buffers(PG)或 Handler_read_*(MySQL)等真实 I/O 指标,而非单次耗时。
查
时为什么耗时不准
因为单次执行受缓存、锁、系统负载干扰太大,直接看
容易误判索引优劣。真正要对比的是「稳定状态下的执行路径和资源消耗」,不是某一次快慢。
务必在相同数据量、相同连接会话、关闭查询缓存(
)下测试
首次执行会触发磁盘读和 buffer pool 加载,至少执行 3 次,取后两次的
平均值
输出里的
行(PostgreSQL)或
状态(MySQL)比耗时更反映索引实际效率
MySQL 下怎么让
显示真实 I/O 开销
默认
不显示磁盘读,得靠
配合清零再执行 SQL,才能看到索引实际扫了多少行、回表几次。
执行前先运行
,再跑目标 SQL
立刻查
:重点关注
(索引扫描行数)、
(回表次数),越小越好
如果
远高于
,说明索引没覆盖查询字段,正在大量随机 IO 回表
PostgreSQL 中
关键看哪几行
重点不是最上面的总耗时,而是每层节点的
和
。
是真磁盘读,>0 就说明索引或表没被缓存住;反复测试时这个值应趋近于 0 才算稳定
如果某层出现
,说明索引选型错误——它扫了 1 万行,只留下 10 行,过滤成本远高于索引收益
注意
后面有没有
和
并存:后者意味着 b 字段没被索引有效利用
SQL 相同但执行计划突变?检查这三处隐性干扰
看起来是同一句 SQL,但执行计划可能因隐式类型转换、统计信息过期、绑定变量窥探而完全不同,导致索引对比失效。
用
确认 WHERE 条件中字段和参数类型严格一致,避免
触发隐式转 cast 导致索引失效
执行
(PostgreSQL)或
(MySQL),确保优化器基于最新数据分布做决策
MySQL 中如果用了预处理语句,第一次执行的执行计划会被复用,加
或改用普通语句重试
实际对比时,最常被忽略的是「索引是否真正减少了物理读」,而不是「执行时间少了多少毫秒」。磁盘 IO 波动大,buffer hit rate 和 filter 效率才是跨环境可复现的判断依据。
EXPLAIN ANALYZEexecution timeSET SESSION query_cache_type = OFFExecution TimeEXPLAIN ANALYZEBuffersHandler_read_*EXPLAINEXPLAINSHOW STATUS LIKE 'Handler_%'FLUSH STATUSSHOW STATUS LIKE 'Handler_read%'Handler_read_nextHandler_read_rndHandler_read_rndHandler_read_nextEXPLAIN (ANALYZE, BUFFERS)Buffers: shared hit=xxx read=yyyRows Removed by Filtershared read=Rows Removed by Filter: 9990/10000Index Scan using idx_a_b on tIndex Cond: (a = $1)Filter: (b > $2)SELECT pg_typeof(col)WHERE id = '123'ANALYZE table_nameANALYZE TABLE table_name/*+ RECOMPILE */