You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何PostgreSQL在关联列已建索引时仍选择顺序扫描?

PostgreSQL关联查询中选择顺序扫描而非索引扫描的原因分析

问题背景

执行以下关联查询,所有关联列已创建索引,但PostgreSQL仍对ZHREPORTFILEHCSVIOLATIONMAPPER表执行顺序扫描:

SELECT * 
FROM ZHREPORTFILEHCSVIOLATIONMAPPER 
JOIN ZHREPORTFILE ON ZHREPORTFILE.REPORT_FILE_AUTO_ID = ZHREPORTFILEHCSVIOLATIONMAPPER.REPORT_FILE_AUTO_ID 
JOIN ZHHCSVIOLATION ON ZHREPORTFILEHCSVIOLATIONMAPPER.HCS_VIOLATION_AUTO_ID = ZHHCSVIOLATION.HCS_VIOLATION_AUTO_ID 
WHERE ZHREPORTFILE.BUILD_AUTO_ID = 2230691610251

执行计划:

Nested Loop  (cost=6764.26..2418291.59 rows=3949 width=439) (actual time=0.664..9689.203 rows=876 loops=1)
  Buffers: shared hit=9539 read=1233401
  ->  Hash Join  (cost=6763.83..2416447.54 rows=3949 width=171) (actual time=0.621..9686.408 rows=876 loops=1)
        Hash Cond: (zhreportfilehcsviolationmapper.report_file_auto_id = zhreportfile.report_file_auto_id)
        Buffers: shared hit=6024 read=1233401
        ->  Seq Scan on zhreportfilehcsviolationmapper  (cost=0.00..2090480.16 rows=85110416 width=24) (actual time=0.018..5667.177 rows=85746659 loops=1)
              Buffers: shared hit=5975 read=1233401
        ->  Hash  (cost=6707.39..6707.39 rows=4515 width=147) (actual time=0.480..0.480 rows=1076 loops=1)
              Buckets: 8192  Batches: 1  Memory Usage: 259kB
              Buffers: shared hit=46
              ->  Index Scan using zhreportfile_fk1_idx on zhreportfile  (cost=0.57..6707.39 rows=4515 width=147) (actual time=0.053..0.294 rows=1076 loops=1)
                    Index Cond: (build_auto_id = '2230691610251'::bigint)
                    Buffers: shared hit=46
  ->  Index Scan using zhhcsviolation_pk on zhhcsviolation  (cost=0.43..0.46 rows=1 width=268) (actual time=0.003..0.003 rows=1 loops=876)
        Index Cond: (hcs_violation_auto_id = zhreportfilehcsviolationmapper.hcs_violation_auto_id)
        Buffers: shared hit=3515
Planning time: 1.802 ms
Execution time: 9689.289 ms

核心原因

1. 顺序IO与随机IO的成本权衡

ZHREPORTFILEHCSVIOLATIONMAPPER是一张拥有8500多万行的超大表。如果走REPORT_FILE_AUTO_ID索引,需要先扫描索引定位匹配行,再回表读取数据,这会产生大量随机IO;而顺序扫描是连续读取全表数据,虽然要遍历所有行,但顺序IO的单位操作成本远低于随机IO,优化器判断这种方式的整体执行成本更低。

2. 统计信息偏差影响成本估算

执行计划中,优化器预估关联后返回3949行,但实际仅返回876行。如果表的统计信息过时,优化器对匹配行数的预估会出现偏差,进而错误判断:认为需要从大表中筛选出较多行,顺序扫描全表后过滤的成本比走索引更低。

3. 索引选择性不足

如果REPORT_FILE_AUTO_ID列的重复度极高(大量行共享同一个值),该索引的选择性就很差。对于选择性差的索引,索引扫描带来的过滤收益不足以抵消随机IO的额外开销,优化器会倾向于选择顺序扫描。

4. Hash Join的执行策略适配

优化器选择将ZHREPORTFILE的查询结果(1076行)构建成Hash表,再与大表做Hash Join。这种场景下,顺序扫描大表并逐行匹配Hash表,比用索引扫描大表再做嵌套循环的效率更高,因为Hash Join在处理大表连接时的开销更低。


内容的提问来源于stack exchange,提问作者DeKabilan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 04:04:57