PostgreSQLの「スナップショット」を理解する:トランザクション分離の裏側にある秘密
「PostgreSQLのトランザクション分離レベル、なんとなくわかったつもりで使ってない?」
後輩からこんな質問を受けたとき、僕はいつもニヤリと笑ってコーヒーを淹れることにしている。SQLの `SET TRANSACTION ISOLATION LEVEL` を書くのは簡単だ。でも、その裏側でPostgreSQLのエンジンがどんな「時間旅行」をしているのかを知っておかないと、本番環境で予期せぬ挙動に泣きを見ることになるからね。
今日は、PostgreSQLのコアアーキテクチャの中でも特に面白い「MVCC(多版同時実行制御)」と、分離レベルがどうやって実現されているのか、その核心に迫っていこう。
—
1. MVCCのエンジンを支える「タイムスタンプ」の正体
PostgreSQLには、行を更新しても古いデータを上書きせず、新しいバージョンとして作り直すという設計思想がある。ここで重要になるのが「どのトランザクションからどのバージョンが見えるのか?」を判定するルールだ。
これを支えているのが、各行に刻まれている `xmin` と `xmax` という隠しカラムだ。
- xmin: その行を生成したトランザクションID(XID)。
- xmax: その行を削除(または更新)したトランザクションID。
PostgreSQLは、今動いているトランザクションの「スナップショット」をメモリ上に保持している。これを判定基準にして、行が見えるか見えないかを厳密にチェックしているんだ。
—
2. 分離レベルごとの「スナップショット取得」戦略
ここが一番のキモだ。分離レベルによって、このスナップショットを「いつ取得するか」が全く違う。
Read Committed(デフォルト)
「ステートメントごとにスナップショットを再取得する」のが最大の特徴だ。
- 挙動: トランザクション内であっても、SQL文(SELECT)を実行するたびに最新のスナップショットを撮り直す。
- 実務への影響: 同じトランザクション内で2回同じSELECTを投げても、その間に他者がコミットしていれば結果が変わる(非反復読み取りが発生する)。「今、この瞬間の最新」を追いかけたいときには最適だね。
Repeatable Read
「トランザクション開始時に一度だけスナップショットを取得する」スタイルだ。
- 挙動: トランザクションが始まってからコミットするまで、世界は固定される。他の誰がデータを書き換えても、君の目には「トランザクション開始時点の静止画」が映り続ける。
- 実務への影響: データの整合性を保ちたいレポート作成や、複数テーブルに跨る一貫した計算処理には必須だ。
Serializable
最も厳格だ。スナップショットだけではなく、「述語ロック(Predicate Locking)」という手法を使って、データの読み取り範囲そのものを保護する。
- 挙動: 読み込んだデータが後から書き換えられる可能性があると判断すると、PostgreSQLはトランザクションを強制的にロールバックさせる(「Could not serialize access」エラー)。
- 実務への影響: 「直列化可能(すべて順番に実行されたのと同じ結果になること)」を保証する。いわゆる「ファントムリード」すら許さない、最強の盾だ。
—
3. 実践:どう使い分けるべきか?
現場でよくあるミスは、何でもかんでも `Serializable` にしてパフォーマンスを落とすことだ。
— 悪い例:全部Serializableにしちゃうと、競合が多発してアプリがエラーだらけになる
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
— … ここで重い処理 …
COMMIT;
僕のアドバイスとしてはこうだ。
1. 基本は `Read Committed` でいけ: ほとんどのWebアプリケーションはこれで十分だ。パフォーマンスと一貫性のバランスが一番いい。
2. 「計算の一貫性」が必要なら `Repeatable Read`: 例えば、決済処理で「口座残高の合計」を計算するようなとき、途中で入金があっても計算結果が揺らいではいけない。そんな時はこいつの出番だ。
3. `Serializable` は最後の手段: 「絶対にデータの衝突を許さない、かつビジネスロジックで排他制御を書きたくない」という極めてクリティカルなケース以外では推奨しない。運用コストが高すぎる。
—
4. 最後に:エンジニアへのエール
PostgreSQLのすごいところは、これほど複雑なMVCCの仕組みを、僕たちが意識しなくてもいいように裏側で完璧に処理してくれるところだ。でも、その仕組みを知っているか知らないかで、デバッグのスピードや設計の深みは全然変わってくる。
「なんでさっきのSELECTと結果が違うんだ?」と悩んだら、それはPostgreSQLが君に「スナップショットの仕組み」について教えてくれているサインだと思ってほしい。
データベースは単なるデータの入れ物じゃない。君のコードが正しく動くための「基盤」だ。その基盤の挙動を深く理解して、誰よりも頼りになるエンジニアを目指してくれ。
また何か気になったら、いつでも聞いてくれよな。現場からは以上だ!
コメント