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

