web-dev-qa-db-ja.com

postgresで既存のテーブルを分割する方法は?

1M +行のテーブルを日付範囲で分割したいと思います。多くのダウンタイムを必要とせずに、またはデータを失うリスクなしに、これはどのように一般的に行われますか?以下は私が検討している戦略ですが、提案を受け付けています。

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

  2. 新しいマスターテーブルと子テーブルを作成します。子テーブルの既存のテーブルにデータのコピーを作成します(データは2か所に存在します)。子テーブルに最新のデータが格納されたら、すべての挿入を新しいマスターテーブルを指すように変更し、既存のテーブルを削除します。

21
Evan Appleby

#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. 本番データベースで更新と挿入を再度有効にします

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

  12. Old_master_backupを削除します

22
Evan Appleby

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

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

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