PostgreSQL 可以在每个查询中使用多个部分索引吗?

Jim*_*Bob 11 postgresql performance index-tuning postgresql-performance

我已经读到 PostgreSQL 通常可以使用多个索引,但是在跨两个索引的查询的特定情况下,它会同时使用两个索引吗?如果是这样,它们是按顺序加载还是一起加载?

例如,如果此查询跨越两个部分索引 by column_1,将如何使用部分索引,以及如何加载和丢弃索引数据:

SELECT 1 FROM sample_table WHERE column_1 > 50 AND column_2 < 50000
Run Code Online (Sandbox Code Playgroud)

Cra*_*ger 17

非常简短的版本:是的,有时。


PostgreSQL 可以使用位图索引扫描来组合多个索引。

像这样的谓词

WHERE a > 50 AND a < 50000
Run Code Online (Sandbox Code Playgroud)

是更一般形式的特化:

wHERE a > 50 and b < 50000
Run Code Online (Sandbox Code Playgroud)

对于 a = b。

PostgreSQL 在这里可以使用两个索引,一个用于谓词的每个部分,然后对AND它们进行位图。如果它们碰巧位于同一列的不同范围内,则无关紧要。

这是很多比单一指标低效,并且可能不适合某些查询是有用的,但它是可能的。

更大的问题是PostgreSQL的部分索引支持不是很亮。不管是否有一个或两个索引,它可能根本不知道它可以使用该索引。

演示设置:

CREATE TABLE partial (x integer, y integer);
CREATE INDEX xs_above_50 ON partial(x) WHERE (x > 50);
CREATE INDEX xs_below_50000 ON partial(x) WHERE (x < 5000);
INSERT INTO partial(x,y) SELECT a, a FROM generate_series(1,100000) a;
Run Code Online (Sandbox Code Playgroud)

好的,对于给定的查询,Pg 会喜欢什么?

regress=> EXPLAIN SELECT y FROM partial WHERE x > 50 AND x < 50000;
                                    QUERY PLAN                                    
----------------------------------------------------------------------------------
 Index Scan using xs_above_50 on partial  (cost=0.29..1788.47 rows=50309 width=4)
   Index Cond: ((x > 50) AND (x < 50000))
(2 rows)

regress=> EXPLAIN SELECT y FROM partial WHERE x > 20 AND x < 50000;
                          QUERY PLAN                          
--------------------------------------------------------------
 Seq Scan on partial  (cost=0.00..1943.00 rows=50339 width=4)
   Filter: ((x > 20) AND (x < 50000))
(2 rows)

regress=> EXPLAIN SELECT y FROM partial WHERE x > 100 AND x < 50000;
                                    QUERY PLAN                                    
----------------------------------------------------------------------------------
 Index Scan using xs_above_50 on partial  (cost=0.29..1787.45 rows=50258 width=4)
   Index Cond: ((x > 100) AND (x < 50000))
(2 rows)

regress=> EXPLAIN SELECT y FROM partial WHERE x > 100 AND x < 20000;
                                   QUERY PLAN                                    
---------------------------------------------------------------------------------
 Index Scan using xs_above_50 on partial  (cost=0.29..710.71 rows=19921 width=4)
   Index Cond: ((x > 100) AND (x < 20000))
(2 rows)
Run Code Online (Sandbox Code Playgroud)

如果我们试图强制执行位图索引扫描只是为了找出 Pg 是否可以使用一个索引扫描,即使对于这种特殊的简单情况和小样本不值得这样做怎么办?

尝试:

regress=> SET enable_seqscan  = off;
SET
regress=> SET enable_indexscan  = off;
SET
regress=> SET enable_indexonlyscan  = off;
SET
regress=> EXPLAIN SELECT y FROM partial WHERE x > 100 AND x < 20000;
                                   QUERY PLAN                                   
--------------------------------------------------------------------------------
 Bitmap Heap Scan on partial  (cost=424.48..1166.30 rows=19921 width=4)
   Recheck Cond: ((x > 100) AND (x < 20000))
   ->  Bitmap Index Scan on xs_above_50  (cost=0.00..419.50 rows=19921 width=0)
         Index Cond: ((x > 100) AND (x < 20000))
(4 rows)
Run Code Online (Sandbox Code Playgroud)

嗯。不。没有在那里组合索引。不过,它可能能够但根本不认为值得扫描第二个索引。

对两个谓词进行 OR 运算的查询呢?

regress=> EXPLAIN SELECT y FROM partial WHERE x > 100 OR x < 200;
                                      QUERY PLAN                                       
---------------------------------------------------------------------------------------
 Bitmap Heap Scan on partial  (cost=1905.29..3848.29 rows=99908 width=4)
   Recheck Cond: ((x > 100) OR (x < 200))
   ->  BitmapOr  (cost=1905.29..1905.29 rows=100000 width=0)
         ->  Bitmap Index Scan on xs_above_50  (cost=0.00..1849.60 rows=99908 width=0)
               Index Cond: (x > 100)
         ->  Bitmap Index Scan on xs_below_50000  (cost=0.00..5.73 rows=193 width=0)
               Index Cond: (x < 200)
(7 rows)
Run Code Online (Sandbox Code Playgroud)

在这里,PostgreSQL 对两个索引进行 OR 运算以找到匹配项,然后进行堆扫描并重新检查。

所以是的,PostgreSQL 可以组合多个部分索引,至少对于某些查询,这样做很有用。

但是,如果我RESET计划者覆盖...

regress=> RESET enable_seqscan;
RESET
regress=> RESET enable_indexscan ;
RESET
regress=> RESET enable_indexonlyscan ;
RESET
regress=> EXPLAIN SELECT y FROM partial WHERE x > 100 OR x < 200;
                          QUERY PLAN                          
--------------------------------------------------------------
 Seq Scan on partial  (cost=0.00..1943.00 rows=99908 width=4)
   Filter: ((x > 100) OR (x < 200))
(2 rows)
Run Code Online (Sandbox Code Playgroud)

... Pg 会意识到顺序扫描表格会更快。

  • 这个答案中有一个非常有影响的错字:CREATE INDEX xs_below_50000 ON partial(x) WHERE (x &lt; 5000); `x &lt; 5000` 应该是 `x &lt; 50000`。更正后,第二个和第三个查询都会获取部分索引。例如,``` 使用 xs_below_50000 对部分进行索引扫描(成本 = 0.29..1659.47 行 = 50219 宽度 = 4)索引条件:(x &gt; 20) (2认同)