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
相关产品推荐
相关产品推荐

