PostgreSQL 在计数期间不使用索引(*)

Cer*_*rin 6 postgresql performance index-tuning postgresql-9.3 postgresql-performance

COUNT(*)在 PostgreSQL 中有一个经常运行的查询,看起来像:

SELECT COUNT(*) 
  FROM customer 
 WHERE source_id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
Run Code Online (Sandbox Code Playgroud)

此查询需要 30-60 秒来运行并搜索数百万条记录。

EXPLAIN ANALYZE 显示它正在执行顺序扫描,因此我创建了索引:

CREATE INDEX customer_by_source ON customer (source_id)
WHERE source_id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
Run Code Online (Sandbox Code Playgroud)

但是,EXPLAIN ANALYZE仍然显示它正在执行顺序扫描并且没有使用索引。

如何加快此查询并使其使用索引?

编辑:我的 Postgres 版本是 9.3.3。该表有大约 2000 万条记录,在每个 source_id 之间平均分配,其中另外 5 条未包含在列表中。

Erw*_*ter 7

如果我正确解释了您添加的信息,您将有 21 个不同的数据 source_id,每个 ( divided pretty evenly among each source_id)大约有 100 万行。这意味着您的查询大约占整个表的 3/4。无论哪种方式,指数通常都买不到太多东西。

测试EXPLAIN(仅用EXPLAIN于此目的)以查看每个变体的估计成本:

EXPLAIN SELECT .... - 您的查询

并记下cost=输出中的所有数字。

然后(仅在您的会话中):

SET enable_seqscan = off;
Run Code Online (Sandbox Code Playgroud)

并重复该程序。之后再次:

SET enable_indexscan = off;
Run Code Online (Sandbox Code Playgroud)

这应该会告诉你为什么 Postgres 选择顺序扫描。通常,如果您的索引远小于表(索引条目远小于表行和/或显着更少的行)并且满足仅索引扫描的条件,则 Postgres 将选择该路线 - 除非您的成本设置严重与您的设置的现实脱节。

最重要的是,可见性地图必须显示整个页面对所有事务都是可见的。VACUUM写入后更新可见性映射。在链接的 Wiki 页面上阅读详细信息。

您可以在运行后立即再次尝试查询VACUUM ANALYZE customer;

如果无法进行仅索引扫描,顺序扫描很可能比位图索引扫描快,这意味着您的索引将毫无用处。

Postgres 9.6 的更新

部分索引有一个重要的限制,在 Postgres 9.6 中有所改进。发行说明:

  • 当索引的子句引用未编入索引的列时,允许对部分索引使用仅索引扫描WHERE(Tomas Vondra、Kyotaro Horiguchi)

例如,由 定义的索引CREATE INDEX tidx_partial ON t(b) WHERE a > 0现在可用于指定WHERE a > 0但不使用的查询的仅索引扫描a。以前这是不允许的,因为a它没有列为索引列。