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

Postgres中DISTINCT ON为何需要额外的Bitmap Heap Scan?

为什么PostgreSQL在Bitmap Index Scan后仍需要Bitmap Heap Scan?

核心原因

Bitmap Index Scan的作用只是定位符合条件的行在磁盘堆文件中的物理位置(堆块编号+行偏移),它并不存储你查询需要的service_id以及其他字段的实际数据——这些数据都保存在表的堆(Heap)里。所以必须通过Bitmap Heap Scan,根据索引提供的位置去堆中读取完整的行数据,才能返回你需要的查询结果。

另外,执行计划里的Recheck Cond步骤是为了验证:索引中找到的行是否真的符合WHERE条件(比如避免已删除行的残留索引条目),以及是否对当前事务可见(MVCC机制的要求)。

针对你的需求优化(获取每个group_id的任意第一条记录)

你的查询用了DISTINCT ON (group_id),PostgreSQL需要先扫描所有符合条件的行、按group_id排序,再保留每个分组的第一行。如果只需要任意一条记录,完全可以优化这个流程,避免全量扫描和排序:

方案1:创建覆盖索引

创建包含group_id和所有查询字段的覆盖索引,这样Bitmap Index Scan就能直接获取所有需要的数据,无需再访问堆文件:

CREATE INDEX ix_services_group_id_covering ON services (group_id) INCLUDE (service_id, ...); -- 将...替换为你实际需要查询的其他字段

使用覆盖索引后,执行计划会跳过Bitmap Heap Scan,直接从索引中读取数据,同时可能避免排序操作。

方案2:用LATERAL子查询定向获取单条记录

针对每个目标group_id单独查询第一条记录,无需扫描所有符合条件的行:

SELECT s.service_id, ...
FROM (VALUES ('67240181b97477f054f9b1bc'), ('67240181b97477f054f9b1be')) AS g(group_id)
LEFT JOIN LATERAL (
  SELECT service_id, ...
  FROM services
  WHERE group_id = g.group_id
  LIMIT 1
) s ON true;

这种方式会对每个group_id单独走索引定位第一条行,在每个分组数据量较大时,效率远高于原查询。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 13:25:05