Unerwarteter Seq-Scan bei Abfragen gegen Boolesche Werte mit dem Wert NULL


10

Ich habe eine Datenbankspalte mit dem Namen " auto_reviewSpaltentyp" boolean. Für dieses Feld gibt es einen Index, der mit dem ActiveRecord-ORM erstellt wurde.

CREATE INDEX index_table_on_auto_renew ON table USING btree (auto_renew);

Wenn ich das Feld nach einem booleschen Wert abfrage, verwendet PG den Index wie erwartet.

EXPLAIN for: SELECT "table".* FROM "table"  WHERE "table"."auto_renew" = 'f'
                                          QUERY PLAN
----------------------------------------------------------------------------------------------
 Bitmap Heap Scan on table  (cost=51.65..826.50 rows=28039 width=186)
   Filter: (NOT auto_renew)
   ->  Bitmap Index Scan on index_domains_on_auto_renew  (cost=0.00..44.64 rows=2185 width=0)
         Index Cond: (auto_renew = false)
(4 rows)

Wenn der Wert ist NULL, wird ein sequentieller Scan verwendet.

EXPLAIN for: SELECT "table".* FROM "table"  WHERE "table"."auto_renew" IS NULL
                           QUERY PLAN
----------------------------------------------------------------
 Seq Scan on table  (cost=0.00..1094.01 rows=25854 width=186)
   Filter: (auto_renew IS NULL)
(2 rows)

Ich bin gespannt auf den Grund für diese Wahl.

Antworten:


19

Im Allgemeinen col IS NULList ein möglicher Kandidat für eine (Standard-) B-Tree-Indexsuche. Das Handbuch :

Eine IS NULLoder IS NOT NULLBedingung für eine Indexspalte kann auch mit einem B-Baum-Index verwendet werden.

Deaktivieren Sie sequentielle Scans, um Beweise zu erhalten (nur in einer Testsitzung!):

SET enable_seqscan = OFF;

Ich zitiere das Handbuch hier :

enable_seqscan (boolean)

Aktiviert oder deaktiviert die Verwendung von sequentiellen Scan-Plan-Typen durch den Abfrageplaner. Es ist unmöglich, sequentielle Scans vollständig zu unterdrücken, aber das Deaktivieren dieser Variablen hält den Planer davon ab, eine zu verwenden, wenn andere Methoden verfügbar sind. Der Standardwert ist aktiviert.

Dann versuchen Sie es erneut:

EXPLAIN ANALYZE SELECT * FROM tbl WHERE auto_renew IS NULL;

Dies führt wahrscheinlich zu einem Bitmap-Index-Scan, der langsamer ist als ein sequentieller Scan in der Tabelle.

Setzen Sie die Sitzung zurück oder schließen Sie sie (die Einstellung ist sitzungslokal).

RESET enable_seqscan;

Indizes für booleanSpalten sind nur in bestimmten Fällen nützlich. Der Planer verwendet einen Index nur, wenn er erwartet, dass dieser schneller ist. Die Berechnungen basieren auf Ihren Kosteneinstellungen und den von gesammelten Statistiken ANALYZE. Wenn ein beträchtlicher Teil der Tabelle Ihrem Zustand entspricht (etwa 5% oder mehr, abhängig davon), ist es normalerweise schneller, stattdessen einen vollständigen Tabellenscan durchzuführen.

Damit bleibt der seltene Wert in einer booleanSpalte der einzige nützliche Kandidat für einen einfachen Index. Und es ist in der Regel effizienten , einen (mehr spezialisiert) zu erstellen Teilindex anstatt für diese - die billiger zu halten ist, kleiner, schneller und leichter , wenn Abfragebedingung Spiel verwendet.

Wenn Sie viele Abfragen haben, nach denen nach Zeilen gesucht wird, auto_renew IS NULLund der NULLFall nicht sehr häufig ist (und / oder Sie eine bestimmte Sortierreihenfolge benötigen), hilft dieser Index, diese Zeilen schnell zu finden / zu sortieren:

CREATE INDEX index_tbl_tbl_id_auto_renew_null ON tbl (tbl_id)
WHERE auto_renew IS NULL;

Die Bedingung des Teilindex muss in der WHEREKlausel einer Abfrage mehr oder weniger genau wiederholt werden , damit der Abfrageplaner erkennt, dass der Index anwendbar ist.

Die indizierte Spalte ( tbl_id) ist eine beliebige Auswahl. Der wichtige Teil ist die WHEREKlausel. Dieser bestimmte Index ist am effektivsten für Abfragen mit ORDER BY tbl_idoder einen zusätzlichen Filter oder Join On tbl_id. Sie könnten es zu einem mehrspaltigen Index machen . Boolesche Spalten sind in Kombination mit anderen häufig nützlicher.

Nebenbei: ORMs sind Krücken, die regelmäßig nicht das volle Potenzial Ihres RDBMS ausschöpfen.


Geniale Antwort, danke Erwin. Ich bin traurig, dass ich es nicht zweimal positiv bewerten kann.
Simone Carletti
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.