EXPLAIN显示Seq Scan不等于未用索引,可能因索引用于ORDER BY或JOIN而非WHERE;真正判断需结合EXPLAIN ANALYZE中的扫描类型与行数估算偏差。

为什么EXPLAIN显示Seq Scan却还在用索引?
执行 EXPLAIN 看到 Seq Scan 并不等于没走索引——它可能只是没在WHERE条件里用,而是在ORDER BY或JOIN中被隐式使用。真正要确认索引是否生效,得看 EXPLAIN ANALYZE 输出里的实际扫描类型和行数估算偏差。常见误判点包括:WHERE字段上有函数(如 WHERE UPPER(name) = 'ABC'),或统计信息过期导致优化器误判选择性。
ANALYZE不是“一键刷新”,得知道什么时候该跑
PostgreSQL的统计信息不会自动实时更新,尤其在大批量UPDATE后,n_dead_tup 暴涨、数据分布突变,旧统计会误导优化器选错执行计划。手动 ANALYZE 有效但需注意:
- 只对目标表运行:
ANALYZE table_name;,避免全库扫描拖慢系统 - 更新后立刻执行,别等autovacuum——它的触发阈值(
autovacuum_analyze_scale_factor)默认是10%,对千万级表意味着要新增百万行才触发 - 若列值分布高度倾斜(如95%的status=0,5%为其他),加列级采样:
ANALYZE table_name (status); - 避免在业务高峰跑,
ANALYZE会持表级读锁(非阻塞但影响并发DML)
UPDATE前必须检查的三个执行计划指标
用 EXPLAIN (ANALYZE, BUFFERS) 跑一次真实UPDATE(加 RETURNING * 或用小范围WHERE测试),重点盯:
-
Rows Removed by Filter:远高于预期行数?说明WHERE没走索引,或索引选择性差 -
Shared Hit/Read缓冲区命中率低于80%?可能是索引或表太大,缓存没覆盖住,得调shared_buffers或预热 -
Planning Time显著高于Execution Time?说明统计不准或查询结构太复杂,优化器花了太多时间做决策
分批UPDATE时,ANALYZE该在哪儿插
不要在每批UPDATE后都跑 ANALYZE——开销大且没必要。正确节奏是:
- 首次执行前:先
ANALYZE,确保初始计划合理 - 中间批次:跳过,靠分批本身控制锁和日志压力
- 全部完成后:再
ANALYZE,为后续查询提供准确统计 - 如果某批UPDATE明显变慢(比如从200ms跳到2s),暂停并查
pg_stat_user_tables的n_dead_tup,高了就先VACUUM再ANALYZE
最易被忽略的是:批量UPDATE本身不改变统计信息,但会大量生成死元组,让后续查询的执行计划持续劣化——这比单次慢更危险。










