Wie partitioniere ich eine vorhandene Tabelle in postgres?

19

Ich möchte eine Tabelle mit mehr als 1 Million Zeilen nach Datumsbereich partitionieren. Wie wird dies in der Regel durchgeführt, ohne dass viel Ausfallzeit erforderlich ist oder das Risiko besteht, dass Daten verloren gehen? Hier sind die Strategien, über die ich nachdenke, die jedoch offen für Vorschläge sind:

  1. Die vorhandene Tabelle ist der Master, und Kinder erben davon. Im Laufe der Zeit werden Daten vom Master zum untergeordneten Element verschoben. In einem bestimmten Zeitraum befinden sich jedoch einige der Daten in der Mastertabelle und andere in den untergeordneten Elementen.

  2. Erstellen Sie eine neue Master- und Kindertabelle. Erstellen Sie eine Kopie der Daten in einer vorhandenen Tabelle in untergeordneten Tabellen (sodass sich die Daten an zwei Stellen befinden). Sobald untergeordnete Tabellen die neuesten Daten enthalten, ändern Sie alle Einfügungen, um auf eine neue Haupttabelle zu verweisen, und löschen Sie die vorhandene Tabelle.

Evan Appleby
quelle
1
Hier meine Ideen: wenn Tabellen Datetime-Spalte haben -> neuen Master erstellen + neues Kind -> neue Daten in NEW + OLD einfügen (Beispiel: Datetime = 2015-07-06 00:00:00) -> von OLD nach NEW-Basis kopieren On-Time-Spalte (wobei: DatumZeit <2015-07-06 00:00:00) -> Tabelle umbenennen -> Insert in NEW ändern, sonst -> "Partitionstrigger" für Insert / Update auf Master erstellen (Insert / Update neuer Daten - > Nach Childs verschieben, damit neue Daten nach Childs eingefügt werden.) -> Master aktualisieren, Trigger verschiebt Daten nach Childs.
Luan Huynh
@Innnh, Sie schlagen also die zweite Option vor, aber nachdem die Daten kopiert wurden, löschen Sie die alte Tabelle und benennen Sie die neue Tabelle so um, dass sie denselben Namen wie die alte Tabelle hat. Ist das richtig?
Evan Appleby
Benennen Sie die neue Tabelle in die alte Tabelle um, aber Sie sollten die alte Tabelle beibehalten, bis die neuen Flow-Partitionstabellen vollständig in Ordnung sind.
Luan Huynh,
2
Für nur wenige Millionen Zeilen halte ich Partitionierung eigentlich nicht für notwendig. Warum glaubst du, brauchst du das? Welches Problem versuchen Sie zu lösen?
a_horse_with_no_name
1
@EvanAppleby DELETE FROM ONLY master_tableist die Lösung.
Dezso

Antworten:

21

Da # 1 das Kopieren von Daten vom Master auf das Kind erfordert, während es sich in einer aktiven Produktionsumgebung befindet, habe ich mich persönlich für # 2 entschieden (Erstellen eines neuen Masters). Dies verhindert Störungen an der Originaltabelle, während diese aktiv verwendet wird. Wenn Probleme auftreten, kann ich den neuen Master problemlos ohne Probleme löschen und die Originaltabelle weiterhin verwenden. Hier sind die Schritte, um es zu tun:

  1. Erstellen Sie eine neue Mastertabelle.

    CREATE TABLE new_master (
        id          serial,
        counter     integer,
        dt_created  DATE DEFAULT CURRENT_DATE NOT NULL
    );
  2. Erstellen Sie Kinder, die vom Master erben.

    CREATE TABLE child_2014 (
        CONSTRAINT pk_2014 PRIMARY KEY (id),
        CONSTRAINT ck_2014 CHECK ( dt_created < DATE '2015-01-01' )
    ) INHERITS (new_master);
    CREATE INDEX idx_2014 ON child_2014 (dt_created);
    
    CREATE TABLE child_2015 (
        CONSTRAINT pk_2015 PRIMARY KEY (id),
        CONSTRAINT ck_2015 CHECK ( dt_created >= DATE '2015-01-01' AND dt_created < DATE '2016-01-01' )
    ) INHERITS (new_master);
    CREATE INDEX idx_2015 ON child_2015 (dt_created);
    
    ...
  3. Kopieren Sie alle historischen Daten in die neue Mastertabelle

    INSERT INTO child_2014 (id,counter,dt_created)
    SELECT id,counter,dt_created
    from old_master
    where dt_created < '01/01/2015'::date;
  4. Neue Einfügungen / Aktualisierungen der Produktionsdatenbank vorübergehend anhalten

  5. Kopieren Sie die neuesten Daten in die neue Mastertabelle

    INSERT INTO child_2015 (id,counter,dt_created)
    SELECT id,counter,dt_created
    from old_master
    where dt_created >= '01/01/2015'::date AND dt_created < '01/01/2016'::date;
  6. Benennen Sie die Tabellen um, sodass new_master zur Produktionsdatenbank wird.

    ALTER TABLE old_master RENAME TO old_master_backup;
    ALTER TABLE new_master RENAME TO old_master;
  7. Fügen Sie old_master die Funktion für INSERT-Anweisungen hinzu, damit die Daten an die richtige Partition übergeben werden.

    CREATE OR REPLACE FUNCTION fn_insert() RETURNS TRIGGER AS $$
    BEGIN
        IF ( NEW.dt_created >= DATE '2015-01-01' AND
             NEW.dt_created < DATE '2016-01-01' ) THEN
            INSERT INTO child_2015 VALUES (NEW.*);
        ELSIF ( NEW.dt_created < DATE '2015-01-01' ) THEN
            INSERT INTO child_2014 VALUES (NEW.*);
        ELSE
            RAISE EXCEPTION 'Date out of range';
        END IF;
        RETURN NULL;
    END;
    $$
    LANGUAGE plpgsql;
  8. Trigger hinzufügen, damit die Funktion für INSERTS aufgerufen wird

    CREATE TRIGGER tr_insert BEFORE INSERT ON old_master
    FOR EACH ROW EXECUTE PROCEDURE fn_insert();
  9. Setzen Sie den Einschränkungsausschluss auf EIN

    SET constraint_exclusion = on;
  10. Aktivieren Sie UPDATES und INSERTS in der Produktionsdatenbank erneut

  11. Richten Sie Trigger oder Cron so ein, dass neue Partitionen erstellt und die Funktion aktualisiert wird, um der richtigen Partition neue Daten zuzuweisen. In diesem Artikel finden Sie Codebeispiele

  12. Löschen Sie old_master_backup

Evan Appleby
quelle
1
Nizza schreiben. Es wäre interessant, wenn das Ihre Anfragen tatsächlich schneller macht. 10 Millionen sind immer noch nicht so viele Zeilen, wie ich mir vorstellen würde, zu partitionieren. Ich frage mich, ob Ihre Leistungseinbußen möglicherweise darauf zurückzuführen vacuumsind, dass Sie aufgrund von Sitzungen mit "inaktiven Transaktionen" nicht aufgeholt oder verhindert wurden.
ein_Pferd_mit_keinem_Namen
@a_horse_with_no_name, bis jetzt hat es die Abfragen nicht wesentlich verbessert :( Ich verwende Heroku mit automatischen Vakuumeinstellungen und es scheint täglich für diesen großen Tisch zu passieren. Werde mehr darüber nachdenken.
Evan Appleby
Sollten die Einfügungen in Schritt 3 und 5 nicht die Tabelle new_master enthalten und postgresql die richtige untergeordnete Tabelle / Partition auswählen lassen?
Pakman
@pakman Die Funktion zum Zuweisen des richtigen Kindes wird erst in Schritt 7 hinzugefügt
Evan Appleby
4

Es gibt ein neues Tool namens pg_pathman ( https://github.com/postgrespro/pg_pathman ), das dies automatisch für Sie erledigt .

So etwas wie das Folgende würde es tun.

SELECT create_range_partitions('master', 'dt_created', 
   '2015-01-01'::date, '1 day'::interval);
kakoni
quelle