PostgreSQL: ポイントインタイムリカバリ(PITR) と Transaction ID

PostgreSQL の「ポイントインタイムリカバリ(PITR)」について調べていて、ドキュメントの中に気になる記述を見つけた。

もし以前のある時点まで復旧させたい場合(例えば、経験不足のデータベース管理者が主トランザクションテーブルを消去した直前)、recovery.confに要求する停止時点を指定するだけです。 停止時点は、「recovery target」として既知の停止時点で指定することも、日付と時刻で指定することも、リストアポイントか完了した特定のトランザクションIDで指定することもできます。 本ドキュメントの執筆時点では使用するトランザクションIDの識別を補助するツールがありませんので、ほとんどの場合は日付と時刻による指定のみを使用することになるでしょう。 https://www.postgresql.jp/document/9.6/html/continuous-archiving.html

データベースに対して何か致命的な操作をしてしまったとき、例えば WHERE を付けずに DELETE してしまったときに、その操作の直前の状態にデータベースを戻したいとする。

このドキュメントによれば、recovery.conf に復旧したい時刻を指定すべきであるそうだ。例えば、こんな風に。

recovery_target_time = '2017-05-02 09:00:00 JST'

私は recovery.confrecovery_target_xid という設定項目があることを知っていたので、不思議に思った。致命的な操作のトランザクションID(xid)を調べて、そのひとつ前のトランザクションIDをここに指定すればいいのではないか。

しかし、これはふたつの理由でうまく行かない。

ひとつの理由は、さきほどの引用にもある通り「トランザクションIDの識別を補助するツール」が存在しないためである。

もうひとつの理由は、「トランザクションIDはトランザクションの開始時に順番に割り振られ、トランザクションはそれとは異なる順番で完了し得る(PosgreSQL のドキュメント)」からである。


トランザクションIDの識別に関しては、ログにトランザクションIDとSQLステートメントを記録すればいいんじゃないかとも考えたけれど、これもダメである。

postgresql.conflog_line_prefix という設定項目があり、ここに %x というプレースホルダーを指定すれば「トランザクションID」が記録されるのだが、トランザクション内で 2 番目以降に発行された変更ステートメントにしか記録されない、という重大な制限がある。

http://stackoverflow.com/q/39585279/513554 を参照。

本当かなぁと疑って、私自身で実際にやってみたけれど、その通りであった。トランザクション内の 1 番目に発行されたステートメントの「トランザクションID」は 0 と記録される。