2

Ich habe eine große Tabelle in Postgres.Postgres Partition Beschneiden

Der Tabellenname ist bigtable und die Spalten sind:

integer |timestamp |xxx |xxx |...|xxx 
category_id|capture_time|col1|col2|...|colN 

ich partitioniert haben in der Tabelle auf Modulo 10 von category_id und das Datum Teil der capture_time Spalte.

Die Partitionstabellen wie folgt aussehen:

CREATE TABLE myschema.bigtable_d000h0(
    CHECK (category_id%10=0 AND capture_time >= DATE '2012-01-01' AND capture_time < DATE '2012-01-02') 
) INHERITS (myschema.bigtable); 

CREATE TABLE myschema.bigtable_d000h1(
    CHECK (category_id%10=1 AND capture_time >= DATE '2012-01-01' AND capture_time < DATE '2012-01-02') 
) INHERITS (myschema.bigtable); 

Wenn ich eine Abfrage mit category_id und capture_time in der where-Klausel ausführen, werden die Partitionen nicht wie erwartet zurückgeschnitten.

explain select * from bigtable where capture_time >= '2012-01-01' and capture_time < '2012-01-02' and category_id=100; 

"Result (cost=0.00..9476.87 rows=1933 width=216)" 
" -> Append (cost=0.00..9476.87 rows=1933 width=216)" 
"  -> Seq Scan on bigtable (cost=0.00..0.00 rows=1 width=210)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 
"  -> Seq Scan on bigtable_d000h0 bigtable (cost=0.00..1921.63 rows=1923 width=216)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 
"  -> Seq Scan on bigtable_d000h1 bigtable (cost=0.00..776.93 rows=1 width=218)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 
"  -> Seq Scan on bigtable_d000h2 bigtable (cost=0.00..974.47 rows=1 width=216)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 
"  -> Seq Scan on bigtable_d000h3 bigtable (cost=0.00..1351.92 rows=1 width=214)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 
"  -> Seq Scan on bigtable_d000h4 bigtable (cost=0.00..577.04 rows=1 width=217)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 
"  -> Seq Scan on bigtable_d000h5 bigtable (cost=0.00..360.67 rows=1 width=219)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 
"  -> Seq Scan on bigtable_d000h6 bigtable (cost=0.00..1778.18 rows=1 width=214)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 
"  -> Seq Scan on bigtable_d000h7 bigtable (cost=0.00..315.82 rows=1 width=216)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 
"  -> Seq Scan on bigtable_d000h8 bigtable (cost=0.00..372.06 rows=1 width=219)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 
"  -> Seq Scan on bigtable_d000h9 bigtable (cost=0.00..1048.16 rows=1 width=215)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100))" 

Allerdings, wenn ich die genauen Modulo Kriterien (category_id%10=0) in der where-Klausel hinzufügen, funktioniert es perfekt

explain select * from bigtable where capture_time >= '2012-01-01' and capture_time < '2012-01-02' and category_id=100 and category_id%10=0; 

"Result (cost=0.00..2154.09 rows=11 width=215)" 
" -> Append (cost=0.00..2154.09 rows=11 width=215)" 
"  -> Seq Scan on bigtable (cost=0.00..0.00 rows=1 width=210)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100) AND ((category_id % 10) = 0))" 
"  -> Seq Scan on bigtable_d000h0 bigtable (cost=0.00..2154.09 rows=10 width=216)" 
"    Filter: ((capture_time >= '2012-01-01 00:00:00'::timestamp without time zone) AND (capture_time < '2012-01-02 00:00:00'::timestamp without time zone) AND (category_id = 100) AND ((category_id % 10) = 0))" 

Gibt es eine Möglichkeit Partition Pruning Arbeit richtig zu machen, ohne die Modulo hinzufügen zu müssen Bedingung in jeder Abfrage?

+0

Welcher Version enthält? Ich denke, es gab einige Verbesserungen im Hinblick auf die Partitionierung in 9.x –

+0

Sie können die Einschränkung ein wenig weniger ausführlich machen: 'CHECK (category_id% 10 = 1 UND date_trunc ('month', capture_time) = '2012-01 -01 ':: date) ' –

+0

@a_horse_with_no_name Ich benutze 9.1 – Dojo

Antwort

1

Für alle, die das gleiche Problem haben: Ich bin zu dem Schluss gekommen, dass der einfachste Weg, um die Abfragen zu ändern out ist, werden Sie mit der Modulo Bedingung category_id%10=0

4

Thing ist: für die Ausschlussbedingung PostgreSQL will create an implicit index. In Ihrem Fall ist dieser Index ein partieller, da Sie einen Ausdruck für die Spalte verwenden, nicht nur ihren Wert. Und es ist in den documentation (suchen Sie nach dem 02.11 Beispiel) angegeben:

PostgreSQL nicht ein ausgeklügeltes Theorembeweisers hat, die mathematisch äquivalente Ausdrücke erkennen kann, die in verschiedenen Formen geschrieben werden. (Ein solcher allgemeiner Theorembeweiser ist nicht nur extrem schwierig zu erstellen, er wäre wahrscheinlich zu langsam, um wirklich nützlich zu sein.) Das System kann einfache Ungleichheitsimplikationen erkennen, zum Beispiel "x < 1" impliziert "x < 2"; andernfalls muss die Prädikatbedingung genau mit einem Teil der WHERE-Bedingung der Abfrage übereinstimmen oder der Index wird nicht als verwendbar erkannt. Die Zuordnung erfolgt zur Abfrageplanungszeit und nicht zur Laufzeit.

Also Ihre Ergebnisse - Sie sollten exakt denselben Ausdruck haben, den Sie beim Erstellen der CHECK-Einschränkung verwendet haben.

Für HASH-basierte Partitionierung ziehe ich es 2 Ansätze:

  • ein Feld hinzufügen, die eine begrenzte Anzahl von Werten annehmen kann (10 in diesem Fall), am besten, wenn diese eine solche existiert nach Design;
  • Hash angeben reicht die gleiche Art und Weise Sie Zeitstempel angeben Bereiche: MINVALUE < = category_id < MAXVALUE

Auch ist es möglich, eine 2-Level-Partitionierung zu erstellen:

  • auf der ersten Ebene, die Sie Erstellen Sie 10 Partitionen basierend auf der Kategorie_ID HASH;
  • auf der zweiten Ebene erstellen Sie die erforderliche Anzahl von Partitionen basierend auf Ihren Zeiträumen.

Obwohl ich immer versuchen, nur 1 Spalte für die Partitionierung zu verwenden, einfacher zu verwalten.

+0

danke für Ihre Eingaben. Der Code, den ich gepostet habe, macht eine Partitionierung auf zwei Ebenen mit einer Vererbungsebene. Leistungsmäßig läuft es schneller als die tatsächliche Vererbung auf zwei Ebenen. Ich weiß, dass es andersherum schneller sein sollte (so wie Sie es vorgeschlagen haben), weil auf der ersten Ebene weniger Tabellen zu überprüfen sind und auf der nächsten Ebene nur Tabellen gescannt werden müssen, die von den qualifizierenden Tabellen der ersten Ebene stammen. Aber in der Praxis ist es langsamer. – Dojo

+0

Was es verlangsamt, ist die Partitionsbereinigungslogik und nicht tatsächliche Tabellenscans. In beiden Fällen korrigiert der Optimizer die Tabellen korrekt, aber die Entscheidung, welche Partitionen zu bereinigen sind, dauert im Falle einer Vererbung auf zwei Ebenen länger. – Dojo

+0

Eine interessante Sache bei der 2-Level-Partitionierung ist, dass Sie die Level-1-Tabelle abfragen können und nun das Löschen von Level-2-Partitionen weniger Zeit benötigt. Ich kann damit Daten so archivieren, dass die Performance nicht beeinträchtigt wird. – Dojo