Postgres: Abfrageplaner, der die Tabellenvererbungsbeschränkung bei der Abfrage nach Null nicht berücksichtigt

7

Ich habe ein Problem mit langsamen Abfragen, das dadurch verursacht wird, dass der Planer jede geerbte Tabelle überprüft, anstatt nur die mit der Einschränkung.

Ich habe eine Tabelle mit 0 Zeilen namens "search_result". Diese Tabelle enthält einige geerbte Tabellen mit unterschiedlichen Einschränkungen basierend auf der "Polarisation", in der sich alle unsere Daten befinden. Zum Beispiel:

CREATE TABLE search_result_positive
(
  CONSTRAINT search_result_positive_polarization_check CHECK (polarization = (1))
)
INHERITS (search_result);

CREATE TABLE search_result_negative
(
  CONSTRAINT search_result_negative_polarization_check CHECK (polarization = (-1))
)
INHERITS (search_result);

CREATE TABLE search_result_unpolarized
(
  CONSTRAINT search_result_unpolarized_polarization_check CHECK (polarization IS NULL)
)
INHERITS (search_result);

Wenn ich beispielsweise eine Abfrage mit "WHERE polarization = 1" ausführe, zeigt der Abfrageplaner an, dass nur die Tabelle "search_result_positive": überprüft wurde, was das gewünschte Verhalten ist.

Wenn die Abfrage jedoch "WHERE polarization NULL" lautet, wird jede einzelne Tabelle überprüft, was immens viel Zeit in Anspruch nimmt. Hier ist ein Beispiel für "search_result_positive":

SELECT  "search_result".* FROM "search_result"  WHERE (polarization = 1)  ORDER BY published_on DESC LIMIT 20;

Limit  (cost=0.44..17.65 rows=20 width=2027) (actual time=3.638..3.666 rows=20 loops=1)
  ->  Merge Append  (cost=0.44..249453.67 rows=289872 width=2027) (actual time=3.637..3.663 rows=20 loops=1)
        Sort Key: search_result.published_on DESC
        ->  Sort  (cost=0.01..0.02 rows=1 width=2882) (actual time=0.004..0.004 rows=0 loops=1)
              Sort Key: search_result.published_on DESC
              Sort Method: quicksort  Memory: 25kB
              ->  Seq Scan on search_result  (cost=0.00..0.00 rows=1 width=2882) (actual time=0.002..0.002 rows=0 loops=1)
                    Filter: (polarization = 1)
        ->  Index Scan Backward using ix_search_result_positive_published_on_asc on search_result_positive  (cost=0.42..245830.25 rows=289871 width=2027) (actual time=3.633..3.658 rows=20 loops=1)
              Filter: (polarization = 1)
Planning time: 2.640 ms
Execution time: 3.762 ms

Und jetzt passiert Folgendes, wenn wir nach null fragen:

SELECT  "search_result".* FROM "search_result"  WHERE (polarization IS NULL)  ORDER BY published_on DESC LIMIT 20;

Limit  (cost=1.93..143.83 rows=20 width=1758) (actual time=205790.210..206266.419 rows=20 loops=1)
  ->  Merge Append  (cost=1.93..462744.74 rows=65221 width=1758) (actual time=205790.208..206266.403 rows=20 loops=1)
        Sort Key: search_result.published_on DESC
        ->  Sort  (cost=0.01..0.02 rows=1 width=2882) (actual time=0.006..0.006 rows=0 loops=1)
              Sort Key: search_result.published_on DESC
              Sort Method: quicksort  Memory: 25kB
              ->  Seq Scan on search_result  (cost=0.00..0.00 rows=1 width=2882) (actual time=0.001..0.001 rows=0 loops=1)
                    Filter: (polarization IS NULL)
        ->  Index Scan using ix_search_result_hybrid_published_on_desc on search_result_hybrid  (cost=0.14..44.44 rows=1 width=2882) (actual time=0.002..0.002 rows=0 loops=1)
              Filter: (polarization IS NULL)
        ->  Index Scan Backward using ix_search_result_negative_published_on_asc on search_result_negative  (cost=0.42..126163.49 rows=1 width=2039) (actual time=44646.431..44646.431 rows=0 loops=1)
              Filter: (polarization IS NULL)
              Rows Removed by Filter: 174428
        ->  Index Scan Backward using ix_search_result_neutral_published_on_asc on search_result_neutral  (cost=0.42..53749.59 rows=1 width=1996) (actual time=29539.393..29539.393 rows=0 loops=1)
              Filter: (polarization IS NULL)
              Rows Removed by Filter: 115678
        ->  Index Scan using ix_search_result_no_apply_published_on_desc on search_result_no_apply  (cost=0.14..44.44 rows=1 width=2882) (actual time=0.003..0.003 rows=0 loops=1)
              Filter: (polarization IS NULL)
        ->  Index Scan Backward using ix_search_result_positive_published_on_asc on search_result_positive  (cost=0.42..245105.57 rows=1 width=2027) (actual time=131590.509..131590.509 rows=0 loops=1)
              Filter: (polarization IS NULL)
              Rows Removed by Filter: 295475
        ->  Index Scan using ix_search_result_unpolarized_published_on_desc on search_result_unpolarized  (cost=0.29..35643.06 rows=65215 width=1758) (actual time=13.863..490.048 rows=20 loops=1)
              Filter: (polarization IS NULL)
Planning time: 1.197 ms
Execution time: 206266.593 ms

Das macht mich verrückt. Ich verstehe nicht, warum jede Tabelle überprüft wird, anstatt nur "search_result_unpolarized". Das macht mich schon verrückt. Die einzige andere Möglichkeit, die ich dafür habe, besteht darin, einen Teil unseres Systems neu zu schreiben, um nur eine bestimmte Tabelle abzufragen, anstatt Postgres entscheiden zu lassen, was eine angemessene Menge an Arbeit sein wird. Es muss einen besseren Weg geben. Jede Hilfe oder Ideen wäre sehr dankbar.

Wir verwenden Postgres 9.3.19 auf Amazon RDS

MarceloJ
quelle
Soll das in PG 9.3 funktionieren?
Colin 't Hart
2
Auch 9.3 ist sehr alt. Es wird dringend empfohlen, ein Upgrade auf 10 durchzuführen.
Colin 't Hart
Ich denke, das ist eine Einschränkung der alten vererbungsbasierten Partitionierung.
a_horse_with_no_name
1
Ich stimme zu, wir müssen aktualisieren. Ich habe gerade überprüft, wir haben eine andere Datenbank mit dem gleichen Schema auf 9.6, das gleiche Problem tritt auf. Was mich jedoch verwirrt, ist, warum es mit Polarisation = 1 funktioniert, aber nicht Polarisation ist null
MarceloJ
1
Ich weiß, dass es fast 1 Jahr ist, aber PostgreSQL scannt alle untergeordneten Tabellen, weil Sie Nullwerte haben. Sie müssen eine weitere Konstante hinzufügen, check (polarization is not null)aber zuerst müssen Sie Nullwerte aus untergeordneten Tabellen verschieben.
Yavuz Selim

Antworten:

1

Der Ausschluss von Einschränkungen weist einige Einschränkungen auf. Dieser Fall (dass Nicht-NULL-Ness nicht berücksichtigt wird, selbst wenn die vorhandene Prüfung dies impliziert) ist in der Dokumentation nicht aufgeführt , ist aber auch in späteren Versionen noch vorhanden. Die Lösung besteht darin, eine weitere Prüfung hinzuzufügen, die NULL-Werte explizit ausschließt:

ALTER TABLE search_result_positive ADD CHECK (polarization IS NOT NULL);
ALTER TABLE search_result_negative ADD CHECK (polarization IS NOT NULL);
dezso
quelle