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

PostgreSQL为何对timestamp字段不执行仅索引扫描?

问题背景与疑问

我有如下分区表:

Table "public.partitioned_11"
     Column     |            Type             | Collation | Nullable |           Default                                                        
----------------+-----------------------------+-----------+----------+----------------------------
 id             | integer                     |           | not null |
 integer_value  | integer                     |           |          |
 invalidated_at | timestamp without time zone |           |          | 
 datetime_value | timestamp without time zone |           |          | 
Partition of: partitioned FOR VALUES IN (11)

以及两个部分索引:

"idx1" btree (datetime_value) INCLUDE (integer_value) WHERE partitioned_by = 11 AND invalidated_at IS NULL
"idx2" btree (id) INCLUDE (integer_value) WHERE partitioned_by = 11 AND invalidated_at IS NULL

当通过id筛选integer_value时,查询执行符合预期的Index Only Scan:

explain select integer_value from partitioned where partitioned_by = 11 and invalidated_at is null and id = 1;
                                            QUERY PLAN                                             
--------------------------------------------------------------------------------------------------
 Index Only Scan using idx2 on partitioned_11 partitioned  (cost=0.43..8.50 rows=4 width=4)
   Index Cond: (id = 1)

但通过datetime_value(timestamp类型)执行相同查询时,PostgreSQL未选择仅索引扫描:

explain select integer_value from partitioned where partitioned_by = 11 and invalidated_at is null and datetime_value = '2020-01-01';
                                                                QUERY PLAN                                                                 
------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on partitioned_11 partitioned  (cost=5.68..618.81 rows=161 width=4)
   Recheck Cond: ((datetime_value = '2020-01-01 00:00:00'::timestamp without time zone) AND (partitioned_by = 11) AND (invalidated_at IS NULL))
   ->  Bitmap Index Scan on idx1  (cost=0.00..5.64 rows=161 width=0)
         Index Cond: (datetime_value = '2020-01-01 00:00:00'::timestamp without time zone)

执行set enable_bitmapscan = false;后,查询才会执行仅索引扫描。我想了解:

  • 该现象的原因是什么?
  • 是否存在位图索引扫描优于仅索引扫描的场景?
  • 这是否与timestamp数据类型本身有关?
问题解答

1. 现象原因

PostgreSQL查询优化器完全基于成本估算选择执行计划:

  • 针对id=1的查询,优化器估算仅返回4行,此时Index Only Scan成本更低——直接从索引中获取所需数据,无需回表,少量行的情况下逐行遍历索引的开销远低于位图构建+堆扫描的组合。
  • 针对datetime_value='2020-01-01'的查询,优化器估算返回161行,此时它认为位图扫描的成本更优:位图索引扫描先批量定位符合条件的行位置,再按物理顺序批量访问堆表,相比逐行执行Index Only Scan(即便无需回表),批量IO的效率在行数较多时更高,优化器的成本模型判定这种方式总成本更低。

需要说明的是,idx1已经包含了查询所需的所有列,理论上完全支持Index Only Scan,最终没被选中纯粹是成本估算的结果。

2. 位图索引扫描优于仅索引扫描的场景

存在以下典型场景:

  • 返回行数较多时:当查询命中大量行,位图扫描可将索引中找到的行位置合并去重,再按物理顺序批量访问堆表,大幅减少随机IO次数。而Index Only Scan即便无需回表,大量行的逐行遍历开销(加上可能的可见性检查)会高于位图扫描的批量处理。
  • 多索引联合查询时:比如同时用两个不同索引的筛选条件,位图扫描可将两个索引的结果位图做逻辑运算(AND/OR),再统一访问堆表,这种方式比单独用某个索引做Index Only Scan或全表扫描效率高得多。

3. 与timestamp数据类型无关

该现象和timestamp类型没有直接关联,核心差异在于两个查询的返回行数估算:id是低基数列(估算返回4行),而datetime_value单个值命中的行数较多(估算返回161行)。哪怕把datetime_value换成integer类型,只要优化器估算返回行数足够多,同样会优先选择位图扫描而非Index Only Scan。

内容的提问来源于stack exchange,提问作者Karel Jílek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 07:22:51