Kann PostgreSQL mehrere Teilindizes pro Abfrage verwenden?

7

Ich habe gelesen, dass PostgreSQL im Allgemeinen mehrere Indizes verwenden kann, aber im speziellen Fall einer Abfrage, die sich über zwei Indizes erstreckt, werden beide verwendet? Wenn ja, werden sie nacheinander oder zusammen geladen?

Wenn sich diese Abfrage beispielsweise über zwei Teilindizes erstreckt column_1, wie werden Teilindizes verwendet, wenn überhaupt, und wie werden die Indexdaten geladen und verworfen:

SELECT 1 FROM sample_table WHERE column_1 > 50 AND column_2 < 50000
Jim Bob
quelle
1
Ich glaube nicht, dass dies allgemein beantwortet werden kann. Können Sie uns die Definitionen dieser Teilindizes zeigen?
Jjanes
Ich denke, es hängt alles von der Datenverteilung ab. Ich würde annehmen, dass die Wahl, mehr als einen Index zu verwenden, nicht davon abhängt, ob es sich um einen Teilindex handelt oder nicht, sondern ob dies die Kosten der Abfrage senkt. Der einzige Weg, um herauszufinden , ist läuft explain analyzeauf Ihrer Abfrage mit Ihren Daten. Ich habe die Erfahrung gemacht, dass Postgres zwei Indizes nur dann bevorzugt, wenn orBedingungen betroffen sind. Ich habe dies bei andBedingungen kaum jemals gesehen .
a_horse_with_no_name
@a_horse_with_no_name Danke. Nach Ihrem letzten Kommentar bedeutet dies, dass Sie tatsächlich mehrere Teilindizes gesehen haben, die für andBedingungen verwendet wurden?
Jim Bob
@jjanes Ich habe keine. Ich versuche eher festzustellen, ob das Innenleben von PostgreSQL dies unter bestimmten Umständen tun würde.
Jim Bob
Tatsächlich habe ich kaum jemals mehrere Indizes gesehen, die für eine andBedingung verwendet wurden (zumindest kann ich mich nicht daran erinnern)
a_horse_with_no_name

Antworten:

14

Sehr kurze Version: Ja, manchmal.


PostgreSQL kann Bitmap-Index-Scans verwenden, um mehrere Indizes zu kombinieren.

Ein Prädikat wie

WHERE a > 50 AND a < 50000

ist eine Spezialisierung der allgemeineren Form:

wHERE a > 50 and b < 50000

für a = b.

PostgreSQL kann hier zwei Indizes verwenden, einen für jeden Teil des Prädikats, und diese dann einer Bitmap ANDzuordnen. Es spielt keine Rolle, ob sie sich in verschiedenen Bereichen derselben Spalte befinden.

Dies ist viel weniger effizient als ein einzelner Index und für einige Abfragen möglicherweise nicht hilfreich, aber möglich.

Das größere Problem ist, dass die teilweise Indexunterstützung von PostgreSQL nicht sehr gut ist. Unabhängig davon, ob es einen oder zwei Indizes gibt, kann es sein, dass der Index überhaupt nicht verwendet werden kann.

Demonstrationsaufbau:

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;

OK, was wird Pg für gegebene Anfragen bevorzugen?

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)

Was ist, wenn wir versuchen, einen Bitmap-Index-Scan zu erzwingen, um herauszufinden, ob Pg einen verwenden kann , auch wenn es sich für diesen speziellen einfachen Fall und eine kleine Stichprobe nicht lohnt, dies zu tun?

Versuchen:

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)

Hm. Nee. Die Indizes dort nicht kombinieren. Es könnte aber einfach nicht glauben, dass es sich lohnt, einen zweiten Index zu scannen.

Was ist mit einer Abfrage, die stattdessen zwei Prädikate ODER-verknüpft?

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)

Hier hat PostgreSQL beide Indizes ODER-verknüpft, um eine Übereinstimmung zu finden, dann einen Heap-Scan durchgeführt und erneut überprüft.

Ja, PostgreSQL kann zumindest für einige Abfragen mehrere Teilindizes kombinieren, sofern dies sinnvoll ist.

Aber wenn ich RESETden Planer überschreibe ...

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)

... Pg wird erkennen, dass es schneller ist, die Tabelle nur nacheinander zu scannen.

Craig Ringer
quelle
Wirklich eine erstaunliche Antwort. Vielen Dank.
Jim Bob
Eine weitere Einschränkung, die ich gesehen habe, ist, dass es nicht zwei Teilindizes zusammen verwenden kann, wenn es unbedingt beide benötigt. Wenn ich beispielsweise eine Index-Cover-ID <500 und die andere Cover-ID ≥ 500 habe, wähle ich ohne ID-Einschränkung aus, dass die Indizes niemals verwendet werden. Ich habe versucht, das zu tun, damit ich die Indexerstellung irgendwie parallelisieren kann ...
Sudo
2
Diese Antwort enthält einen ziemlich wirkungsvollen Tippfehler: CREATE INDEX xs_below_50000 ON partiell (x) WHERE (x <5000); Das x < 5000sollte sein x < 50000. Wenn dies korrigiert ist, nehmen sowohl die zweite als auch die dritte Abfrage die Teilindizes auf. Beispiel: `` `Index-Scan mit xs_below_50000 auf Teil (Kosten = 0,29..1659,47 Zeilen = 50219 Breite = 4) Indexbedingung: (x> 20)
Tantrix