1

我有一个包含 1m 条记录的表,其中 100k 条记录具有 null on colA。其余记录具有非常不同的值,在此列上创建常规索引与使用 的部分索引是否有区别where colA is not null

由于常规 Postgres 索引不存储 NULL 值,它与创建部分索引不一样where colA is not null吗?
任何一个索引都有什么优点或缺点?

4

2 回答 2

3

如果您创建没有空值的部分索引,它将不会使用它来查找空值。

这是一个关于 13.5 的完整索引的测试。

# create index idx_test_num on test(num);
CREATE INDEX

# explain select count(*) from test where num is null;
                                     QUERY PLAN                                      
-------------------------------------------------------------------------------------
 Aggregate  (cost=5135.00..5135.01 rows=1 width=8)
   ->  Bitmap Heap Scan on test  (cost=63.05..5121.25 rows=5500 width=0)
         Recheck Cond: (num IS NULL)
         ->  Bitmap Index Scan on idx_test_num  (cost=0.00..61.68 rows=5500 width=0)
               Index Cond: (num IS NULL)
(5 rows)

并带有部分索引。

# create index idx_test_num on test(num) where num is not null;
CREATE INDEX

# explain select count(*) from test where num is null;
                                      QUERY PLAN                                      
--------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=10458.12..10458.13 rows=1 width=8)
   ->  Gather  (cost=10457.90..10458.11 rows=2 width=8)
         Workers Planned: 2
         ->  Partial Aggregate  (cost=9457.90..9457.91 rows=1 width=8)
               ->  Parallel Seq Scan on test  (cost=0.00..9352.33 rows=42228 width=0)
                     Filter: (num IS NULL)
(6 rows)

由于常规 postgres 索引不存储 NULL 值...

自 16 年前的 8.2 版 [检查笔记] 以来,情况并非如此。8.2文档说...

默认情况下,索引不用于 IS NULL 子句。在这种情况下使用索引的最佳方法是使用 IS NULL 谓词创建部分索引。

8.3 引入 nulls firstnulls last许多其他围绕空值的改进,包括...

允许 col IS NULL 使用索引 (Teodor)

于 2022-02-20T23:51:01.133 回答
1

这一切都取决于。

NULL自 Postgres 8.3 版本起,值包含在(默认)B-tree 索引中,就像 Schwern 提供的一样。但是,自Postgres 9.0起,您提到的谓词 ( where colA is not null) 才得到适当的支持。发行说明

允许IS NOT NULL限制使用索引 (Tom Lane)

这对于在包含许多空值的索引中查找MAX()/值特别有用。MIN()

GIN 索引紧随其后:

从 PostgreSQL 9.1 开始,空键值可以包含在索引中。

通常,如果部分索引从索引中排除了表的主要部分,则它是有意义的,使其显着变小并节省对索引的写入。由于 B-tree 索引非常浅,因此裸寻道性能可以惊人地扩展(一旦索引被缓存)。减少 10 % 的索引条目在该领域几乎无关紧要。

您的案例将仅排除所有行的 10% 左右,而且很少有回报。部分索引为查询计划器增加了一些开销,并排除了与索引条件不匹配的查询。(如果匹配不是很明显,Postgres 查询规划器不会努力。)

OTOH,Postgres 很少使用索引来检索表的 10% 的谓词 - 顺序扫描通常会更快。再次,这取决于。

如果(几乎)所有查询都排除NULL(以 Postgres 规划器理解的方式),那么仅排除所有行的 10% 的部分索引仍然是一个明智的选择。但如果查询模式发生变化,它可能会适得其反。增加的复杂性可能不值得。

另外值得注意的是,NULL在 Postgres 索引中仍然存在带有值的极端情况。我最近遇到了这种情况,当第一个索引表达式被过滤时,Postgres 证明不愿意从多列索引中读取已排序的行IS NULL(使部分索引更适合这种情况):

db<>在这里摆弄

因此,这取决于完整的图片。

于 2022-02-21T02:24:17.543 回答