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

PostgreSQL为何在该示例中不联合使用两个索引?

PostgreSQL 9.6 为何不联合使用两个索引执行查询?

首先,咱们拆解下你的场景和查询逻辑:你通过计算字段last_event_id模拟订单簿事件快照,创建了表t并建立了两个索引——基于event_id的主键索引t_pkey,以及基于last_event_id的btree索引t_idx。执行查询SELECT * FROM t WHERE event_id <= 80001 AND last_event_id >= 80001;后,查询计划只走了t_pkey索引,过滤掉了绝大多数行,却没联合使用t_idx。这背后主要是PostgreSQL优化器的成本估算逻辑和数据分布特性在起作用,具体原因如下:

1. 优化器认为单索引扫描+过滤的成本更低

PostgreSQL的查询优化器会对比所有可行执行计划的成本,选择代价最小的方案。对于你的查询:

  • 如果选择联合两个索引(比如Bitmap Index Scan),需要先分别从t_pkey和t_idx中获取符合条件的行的位图,再对两个位图做交集运算,最后根据位图去表中读取数据。这个过程涉及多次索引扫描、位图运算和回表,优化器估算下来的总成本可能更高。
  • 而选择单走t_pkey索引,直接扫描所有event_id <= 80001的行(共80001行),然后在内存中过滤出last_event_id >= 80001的行(仅2行),优化器认为这个方案的IO和CPU成本更低——毕竟扫描8万行的开销,加上简单的过滤操作,比多索引联合的复杂流程更划算。

2. last_event_id的索引选择性不足

从你的last_event_id计算逻辑10000*(round(i.event_id/10000,0)+1)来看,这个字段的取值是分段的(一批连续的event_id对应同一个last_event_id),意味着每个last_event_id值会对应大量行,索引的选择性极低。优化器会认为这种低选择性的索引,单独使用或联合使用的价值都不高,不值得为它额外付出索引扫描和位图运算的成本。

3. PostgreSQL 9.6版本的优化器局限性

PostgreSQL 9.6是2016年发布的老版本,其优化器在多索引联合查询的成本估算上,相比新版本(比如12+)不够精准。对于这种满足条件的行数极少、且其中一个索引(主键索引)扫描范围明确的场景,老版本优化器更倾向于选择简单直接的单索引扫描+过滤方案,而不会尝试更复杂的多索引联合。

可以尝试的优化方案

如果你想让查询更高效,可以考虑创建复合索引,让优化器在索引层面完成过滤:

CREATE INDEX t_event_last_idx ON t(event_id, last_event_id);

这个复合索引可以让优化器在扫描event_id <= 80001的过程中,直接在索引层面过滤last_event_id >= 80001的条件,避免回表后再做大量过滤操作,进一步降低查询成本。

或者,如果你想验证多索引联合的效果,可以临时关闭单索引扫描的开关,强制优化器考虑Bitmap扫描:

SET enable_indexscan = off;
EXPLAIN ANALYZE SELECT * FROM t WHERE event_id <= 80001 AND last_event_id >= 80001;
SET enable_indexscan = on;

对比两种计划的执行时间,就能直观看到哪种方案更适合你的数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:38:12