PostgreSQL の PITR(ポイントインタイムリカバリ) を試してみる

PostgreSQL の PITR(ポイントインタイムリカバリ) を試してみる。

大まかな流れ

  • 事前設定
    • WAL archive 設定
    • ベースバックアップの作成
  • 事件発生!
  • リカバリ

WAL archive の設定

通常の WAL は不要になったら(commit/abort 時に)消えてしまうのだが、PITR のために archive directory にためるような設定にする。
通常は耐障害性を考えて、archive ディレクトリは data ディレクトリと別のディスクにすべき。
ディレクトリを作り

% mkdir archive

dbdata/postgresql.conf に archive directory 指定。
ファイルが決して上書きされないように test でチェックしてから archive するように書く。(マニュアル通り)

archive_mode = on
archive_command = 'test ! -f /Users/taro/pgsql/8.3.7.pitr/archive/%f && cp %p /Users/taro/pgsql/8.3.7.pitr/archive/%f'

WAL はトランザクションが完了するなど、安全なタイミングで archive されるのでトラフィックによっては、長い間 archive されない場合もある。

archive_timeout = 60

と設定しておけば、可能ならば60秒おきに強制的に新しい WAL ファイルに切り替わるようになるらしい。

初期バックアップを取る

WAL は以前の状態に対するアクションのログなので、最初の状態をバックアップする必要がある。

まずは pg_start_backup でチェックポイントを作成する。引数は任意の文字列。口授打つのバックアップファイルのパスを指定するのがおすすめと書いてあった。

# SELECT pg_start_backup('dbdata.init.bak');
 pg_start_backup 
-----------------
 0/1000060
(1 row)

この状態で cp コマンドでファイルをバックアップする。

% cp -r dbdata dbdata.init.bak

このとき並行して別の db 操作が動いていても構わない。
cp が終わったら pg_start_backup する。

# SELECT pg_stop_backup();
 pg_stop_backup 
----------------
 0/2000000
(1 row)

いろいろデータを入れてみよう

ちょっとわざとらしいですが、それぞれの操作をした時刻を合わせて取得しておく。

# CREATE TABLE person (id integer, name text);
# select now() ;
              now
-------------------------------
 2009-04-14 16:11:03.546275+09

データ入れる。

=# insert into person values(1, 'higepon');
INSERT 0 1
hige=# insert into person values(2, 'higepon2');
INSERT 0 1
hige=# insert into person values(3, 'higepon3');
INSERT 0 1
hige=# select now() ;
              now
-------------------------------
 2009-04-14 16:12:24.724836+09
(1 row)


さて、そろそろオペレーションミスが発生しそうですね。

# truncate table person;
TRUNCATE TABLE

うわあああ。truncate しちゃった。 rollback も効かないや。

リカバリ

  1. サーバー止める
  2. pg_hba.conf をいじってリカバリ中は他のユーザーがアクセスできないようにする
  3. 念のため data ディレクトリをバックアップする(リカバリに失敗したとき用)
  4. data ディレクトリとそのサブディレクトリにあるファイルをごっそり消す。(こわいなあ)
  5. dbdata.init.bak の中身を dbdata に戻す。(ファイルの所有権に注意)
  6. dbdata/pg_xlog にあるファイルを消す。pg_xlog/archive_status/ は再作成する。
  7. dbdata/recovery.conf を作成する。中身は戻したい時刻。(指定するのはトランザクション ID でも良い)
    1. restore_command = 'cp /Users/taro/pgsql/8.3.7.pitr/archive/%f %p'
    2. recovery_target_time = '2009-04-14 16:13:00 JST'
    3. サーバースタート

ログファイルを見てみると

LOG:  starting archive recovery
LOG:  restore_command = 'cp /Users/taro/pgsql/8.3.7.pitr/archive/%f %p'
LOG:  recovery_target_time = '2009-04-14 16:13:00+09'
cp: /Users/taro/pgsql/8.3.7.pitr/archive/00000001.history: No such file or directory
LOG:  restored log file "000000010000000000000000" from archive
LOG:  automatic recovery in progress
LOG:  redo starts at 0/441308
LOG:  restored log file "000000010000000000000001" from archive
LOG:  restored log file "000000010000000000000002" from archive
LOG:  restored log file "000000010000000000000003" from archive
LOG:  restored log file "000000010000000000000004" from archive
LOG:  restored log file "000000010000000000000005" from archive
LOG:  recovery stopping before commit of transaction 392, time 2009-04-14 16:14:25.909294+09
LOG:  redo done at 0/5002DC4
LOG:  last completed transaction was at log time 2009-04-14 16:12:21.428895+09
cp: /Users/taro/pgsql/8.3.7.pitr/archive/00000002.history: No such file or directory
LOG:  selected new timeline ID: 2
cp: /Users/taro/pgsql/8.3.7.pitr/archive/00000001.history: No such file or directory
LOG:  archive recovery complete
LOG:  database system is ready to accept connections
LOG:  autovacuum launcher started

recovery が成功したとでた。確認してみよう。

# select * from person;
 id |   name   
----+----------
  1 | higepon
  2 | higepon2
  3 | higepon3

復旧した。

注意

  • data ディレクトリにある conf ファイルは元に戻りません。
  • Hash Index への操作が WAL に書かれないので REINDEX が必要
  • CREATE TABLESPACE コマンドの WAL がテーブルスペースを絶対パスで出力する。別マシンで復旧するときは注意。