web-dev-qa-db-ja.com

Postgresデータベースを一時的に読み取り専用にする(ボリュームのスナップショットを実行するため)

PostgreSQLに組み込まれているバックアップメカニズムは、必ずしも非常に適しているとは限りません。 PGデータをバックアップすると同時にバックアップしたい外部データがあるため、アプリケーションを quiescent 状態にしたい場合があります。ただし、アプリケーションを休止状態にする唯一の方法は、データベースも「ロック」することです。 PGには、データベース全体またはクラスター全体のロックメカニズムがありません。 PGを読み取り専用状態にすることは、次のソリューションの一部になります。

  1. アプリケーションデータの静止(ログインを無効にする)
  2. データベースを静止する(読み取り専用にする)
  3. PGチェックポイントまたはpg_xlog_switch()を実行します
  4. アプリとデータボリュームのスナップショットを作成する
  5. データベースを再開します(再度RWにします)
  6. アプリケーションを再開する
  7. スナップショットをバックアップする
7
Otheus

インターネット上の他の場所で回答を選別した後、私は解決策を考案しました。他の答えはそれ自体、不完全でした。ですから、他の人の利益になることを期待して、ここで答えを提示します。

戦略

  1. (クラスターではなく)データベースへの接続を無効にします。
  2. データベースの_default_transaction_read_only_設定をtrueに設定します。
  3. そのデータベースへの既存の接続を終了します。
  4. (読み取り専用)接続を再度有効にします。

それが行われると、(私の解決策では):

  1. CHECKPOINTを実行します(これが最も安全だと思いますが、pg_xlog_switch()は非常に高負荷のサーバーに適しています)
  2. ボリュームのスナップショットを撮る
  3. 前の手順を逆にします。 (しかし、これはトリッキーです!)

落とし穴

  1. トランザクションの途中で接続を終了することは、おそらく悪い考えです。アイドル状態の接続を強制終了し、数秒待ってからアイドル状態の接続を強制終了し、さらに数回待って、すべてがなくなるまで繰り返します。
  2. ある時点で、オープン/ハングしたクエリを強制終了するか、バックアップを中止する必要があります。
  3. セッションのトランザクションの開始時に、Postgresqlはプロセステーブルの一種のスナップショットを取得します。不要なプロセスがまだ残っているかどうかを確認するために移動するたびに、このスナップショットをリセットする必要があります。 pg_stat_clear_snapshot()を参照
  4. 読み取り/書き込み状態の復元はそれほど単純ではないです。読み取り専用接続が存在する場合、新しい読み取り/書き込みステータスを有効にするには、それらを終了する必要があります。しかし、既存の接続を削除している間に新しい接続が到着する可能性があります。もう一度、あなたは

    1. データベースへの接続を無効にする
    2. default_transaction_read_onlyステータスをfalseに変更します
    3. 既存の接続を強制終了します
    4. データベースへの(r/w)接続を再度有効にする

代替戦略

別の戦略は、アプリケーションが使用するロールの権限を変更することです。これはかなり厄介で、一般的ではありません。

たとえば、テーブルだけでなく、シーケンス、ラージオブジェクト、そしておそらくスキーマ自体に対する取り消し/再付与を行う必要があります。さらに、アクセスを変更したときの既存の接続の動作は正確には何ですか?おそらく影響はありません。つまり、これらのバックエンドも強制終了する必要があります。最後に、アプリケーションにはほとんどのテーブルへの読み取り/書き込みアクセス権がありますが、スキーマ内の他のテーブルへのアクセス権はありません。再付与にそれらのオブジェクトも含まれていないことを確認する必要があります。

別の可能性は、カタログをクエリして動的クエリを実行することにより、すべてのテーブルをロックすることです。それは私の好みにとって危険なようでした。

実装

Pause_service

データベースインスタンス名は「gitlabhq」、アプリケーションのユーザー名は「gitlab」です。それをあなた自身のものに置き換えてください:

_  psql -Upostgres  <<'PAUSE_DB'
    -- 1. disable new connections
    alter database gitlabhq_production with allow_connections = off;
    -- 2. Make DB read-only
    alter database gitlabhq set default_transaction_read_only = true;
    -- 3. Inobtrusively but safely terminate current connections
    DO $X$ BEGIN
        -- kill open idle connections, try up to 9x. Last time, kill regardless
        FOR i IN 1..10 LOOP
          PERFORM pg_terminate_backend(pid) from pg_stat_activity where usename = 'gitlab'
            and (i >= 10 OR state in ('idle', 'disabled' ));
          PERFORM pg_stat_clear_snapshot();
          EXIT WHEN NOT EXISTS ( select pid from pg_stat_activity where usename = 'gitlab' );
          RAISE NOTICE 'pg backends still open: sleeping 2 seconds';
          PERFORM pg_sleep(2);
          PERFORM pg_stat_clear_snapshot();
        END LOOP;
        -- send notice if still open connections
        IF EXISTS ( select pid from pg_stat_activity where usename = 'gitlab' ) THEN
            RAISE NOTICE 'Hung backends. Backup might not be 100%% consistent';
        END IF;
    END;$X$;
    -- 4. Allow read-only connections while checkpointing/snapshotting
    alter database gitlabhq with allow_connections = on;
    CHECKPOINT;
_

履歴書

_    alter database gitlabhq_production with allow_connections = off;
    alter database gitlabhq set default_transaction_read_only = false;
    SELECT pg_stat_clear_snapshot();
    SELECT pg_terminate_backend(pid) from pg_stat_activity where usename = 'gitlab';
    alter database gitlabhq with allow_connections = on;
_

この最後のステップで長時間実行の読み取り専用/ SELECTクエリを強制終了する可能性がありますが、私の経験では、そのような長時間実行クエリは数時間ではなくても数分続く可能性があり、稼働時間を確保するためにこれらを強制終了することは許容されます。みんな。

9
Otheus

個人的には、この機能を公式のPostgreSQL機能として持つことが望ましいと思います。

単純にそれを行う

PostgreSQL拡張機能のCコーディングに手を染めたくない場合は、PostgreSQLの前に接続プーラーを置くだけです。 pgBouncerのように。

pgBouncer 組み込みのアプリケーションアクティビティを一時停止する機能があります 。それを非常に便利にするために、(pgbouncer経由ではなく)直接接続し、新しい接続の着信を一時停止したら、アクティブな接続をキャンセルする必要があります。select pg_terminate_backend(pid) from pg_stat_activity where pid <> pg_backend_pid()だけです。

それを正しくする

ただし、手を汚したい場合は、C拡張機能を使用して行うことができます。拡張機能は次の条件を満たす必要があります。

  • _shared_preload_libraries_にロードして、db_is_lockedのようなブールフラグで小さな静的共有メモリセグメントを登録できるようにします。

  • Shmemでis-lockedフラグをテストする_ProcessUtility_hook_と_ExecutorStart_hook_を登録し、設定されている場合は、フラグが再びクリアされるまでWaitLatchループでスリープします。 (代わりにパーサーフックを使用することもできます)。

  • Cで2つのSQL呼び出し可能関数を記述します。1つはフラグを設定します。もう1つはフラグをクリアし、PGPROCを繰り返してすべてのユーザープロセスのラッチを設定するため、ユーザーはすぐにウェイクアップすることがわかります。

  • 必要に応じて、フラグが設定されている場合にPGXACTを反復して3番目の関数を記述し、開いている書き込みトランザクションを見つけて、それらに終了するように通知します。

これはすべて BDR拡張 の一部としてすでに実装されていますが、それははるかに大きなシステムの一部です。関連する部分を独自の拡張機能に抽出する可能性があります。 _bdr_locks.c_、_bdr_commandfilter.c_、_bdr_executor.c_、_bdr.c_などを参照してください。

これにより、PostgreSQLがディスク上で読み取り専用にならないことに注意してください-チェックポインタは引き続き実行され、bgwriterは引き続き実行され、アーカイバは引き続き実行されますしたがって、アトミックファイルシステムのスナップショットまたはpg_start_backup()/pg_stop_backup()なしでDBバックアップを作成するだけでは不十分です。しかし、DBでアプリケーションの活動を一時停止して、ユースケースとしては問題ありません。

4
Craig Ringer