postgresで既存のテーブルをパーティション分割する方法は?


19

日付範囲ごとに100万行以上のテーブルをパーティション分割したいと思います。これは、多くのダウンタイムを必要とせずに、またはデータを失うリスクを負うことなく、通常どのように行われますか?ここに私が検討している戦略がありますが、提案があります:

  1. 既存のテーブルがマスターであり、子はそれを継承します。時間が経つにつれて、マスターから子にデータが移動しますが、データの一部がマスター表にあり、一部が子にある期間があります。

  2. 新しいマスターテーブルと子テーブルを作成します。子テーブルの既存のテーブルにデータのコピーを作成します(したがって、データは2つの場所に存在します)。子テーブルが最新のデータを取得したら、今後すべての挿入を変更して新しいマスターテーブルを指し、既存のテーブルを削除します。


1
ここで私のアイデア:テーブルにdatetime列がある場合->新しいマスター+新しい子を作成する->新しいデータをNEW + OLDに挿入する(例:datetime = 2015-07-06 00:00:00)-> OLDからNEWベースにコピーする時間列(ここで:datetime <2015-07-06 00:00:00)->テーブルの名前変更->挿入をNEWに変更->マスターでの挿入/更新用の「パーティショントリガー」の作成(新しいデータの挿入/更新- >子に移動して、新しいデータが子に挿入されるようにする)-> masterを更新すると、トリガーはデータを子に移動します。
ルアンフイン

@ Innnh、2番目のオプションを提案していますが、データがコピーされたら、古いテーブルを削除し、新しいテーブルの名前を変更して古いテーブルと同じ名前にします。そうですか?
エヴァンアップルビー

新しいテーブルの名前を古いテーブルに変更しますが、新しいフローパーティションテーブルが完全に正常になるまで古いテーブルを保持する必要があります。
ルアンフイン

2
ほんの数百万行の場合、パーティション分割は実際には必要ないと思います。なぜあなたはそれが必要だと思いますか?どのような問題を解決しようとしていますか?
a_horse_with_no_name

1
@EvanAppleby DELETE FROM ONLY master_tableがソリューションです。
-dezso

回答:


21

#1では、アクティブな実稼働環境にいる間にマスターから子にデータをコピーする必要があるため、私は個人的に#2を使用しました(新しいマスターの作成)。これにより、元のテーブルがアクティブに使用されている間、元のテーブルが中断するのを防ぎます。問題がある場合は、新しいマスターを問題なく簡単に削除し、元のテーブルを引き続き使用できます。手順は次のとおりです。

  1. 新しいマスターテーブルを作成します。

    CREATE TABLE new_master (
        id          serial,
        counter     integer,
        dt_created  DATE DEFAULT CURRENT_DATE NOT NULL
    );
  2. マスターから継承する子を作成します。

    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. すべての履歴データを新しいマスターテーブルにコピーする

    INSERT INTO child_2014 (id,counter,dt_created)
    SELECT id,counter,dt_created
    from old_master
    where dt_created < '01/01/2015'::date;
  4. 本番データベースへの新しい挿入/更新を一時的に一時停止します

  5. 最新のデータを新しいマスターテーブルにコピーする

    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. new_masterが本番データベースになるようにテーブルの名前を変更します。

    ALTER TABLE old_master RENAME TO old_master_backup;
    ALTER TABLE new_master RENAME TO old_master;
  7. INSERTステートメントの関数をold_masterに追加して、データが正しいパーティションに渡されるようにします。

    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. INSERTSで関数が呼び出されるようにトリガーを追加します

    CREATE TRIGGER tr_insert BEFORE INSERT ON old_master
    FOR EACH ROW EXECUTE PROCEDURE fn_insert();
  9. 制約の除外をオンに設定します

    SET constraint_exclusion = on;
  10. 本番データベースでUPDATESおよびINSERTSを再度有効にします

  11. トリガーまたはcronを設定して、新しいパーティションを作成し、機能を更新して、新しいデータを正しいパーティションに割り当てます。コード例については、この記事を参照してください

  12. old_master_backupを削除します


1
素晴らしい記事。それが実際にクエリを高速化するのは興味深いでしょう。パーティション化について考えている行の数は、まだ1,000万個ではありません。パフォーマンスの低下はvacuum、「アイドル状態のトランザクション」セッションが原因で追いつかない、または妨げられていることが原因であると思われます。
a_horse_with_no_name

@a_horse_with_no_name、これまでのところそれはクエリが有意に良好:(私は自動真空設定がHerokuのを使用して、この大きなテーブルのために毎日起こるように見える行っていないことカントーに多くなります。。
エヴァン・アップルビー

ステップ3と5の挿入はテーブルnew_masterに行われ、postgresqlに適切な子テーブル/パーティションを選択させるべきではありませんか?
パクマン

@pakman正しい子を割り当てる関数は、ステップ7まで追加されません
エヴァンアップルビー

4

自動的にこれを行うpg_pathman(https://github.com/postgrespro/pg_pathman)と呼ばれる新しいツールがあります。

そのため、次のようなものがそれを行います。

SELECT create_range_partitions('master', 'dt_created', 
   '2015-01-01'::date, '1 day'::interval);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.