- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け
- Nginx受け口
- アプリ処理
- DB保存今ここ
前のレッスンで、古い行版を即座に消さないこととその帰結である保証を見ました。今回はその保証がどこまで効くのかを扱います。ロックの種類の網羅はこの期の後半に置いてあります。
まず結論
「PostgreSQL では読み取りがブロックされない」は言い過ぎです。公式は例外を1つだけ、はっきり書いています——**Only an `ACCESS EXCLUSIVE` lock blocks a `SELECT` (without `FOR UPDATE/SHARE`) statement.** 逆に読めば、**それ以外のロックでは素の `SELECT` は止まりません**。例外は1種類だけです。
止めるのは特定のロックであり、そのロックを取るコマンドは公式に列挙されています。そしてもう一つ——読み取りが書き込みを待たないことは、**書き込み同士が待たないこと**を意味しません。公式は後から更新しようとした側について “will wait for the first updating transaction to commit or roll back” と書いています。
`FOR UPDATE` も `FOR SHARE` も付けない素の `SELECT` を止めてしまうロックは、何種類あるでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
保証には範囲がある — それを公式が自分で書いている
前のレッスンで、古い行版を即座に消さないことと、その帰結である reading never blocks writing and writing never blocks reading を見ました。この一文は強く、覚えやすく、そして広げすぎやすいものです。「PostgreSQL では読み取りがブロックされない」という要約が流通しますが、公式はそこまで書いていません。
Explicit Locking のページに、例外が1つ置かれています。
Only an
ACCESS EXCLUSIVElock blocks aSELECT(withoutFOR UPDATE/SHARE) statement.
Only から始まる文は、二つのことを同時に述べます。ACCESS EXCLUSIVE は素の SELECT を止める。そしてそれ以外のロックは止めない。だからこれは保証を壊す話ではなく境界線を引く話です。前のレッスンの保証は依然として本物で、そのうえで例外が1種類ある、というのが正確な形です。
括弧書きも読み落とせません。without FOR UPDATE/SHARE とあるので、対象は何も付けない素の SELECT です。FOR UPDATE や FOR SHARE を付けたものは守備範囲の外です。
なぜ ACCESS EXCLUSIVE だけが特別なのか。同じページがモードの性質を書いています。
ACCESS EXCLUSIVE... Conflicts with locks of all modes... This mode guarantees that the holder is the only transaction accessing the table in any way.
all modes と衝突し、保持者が the only transaction accessing the table in any way になります。前のレッスンの土台は「読み用と書き用のロックが衝突しない」ことでした。すべてと衝突するモードが1つあれば、その間だけ土台が成り立たなくなる——これが例外の正体です。本教材が参照するのは PostgreSQL 18 時点の公式ドキュメントです。
素の `SELECT`(`FOR UPDATE` / `FOR SHARE` なし)を止めるロックについて、公式の記述はどれですか。
読み取りが待たないことと、書き込みが待たないことは別
もう一つの広げすぎは、「読み取りが書き込みを止めない」から「何も待たない」へ滑る道です。Transaction Isolation のページは、同じ行を2つのトランザクションが更新しようとした場合をこう書いています。
In this case, the would-be updater will wait for the first updating transaction to commit or roll back (if it is still in progress).
待ちます。しかも待った後がそのまま書き込みではありません。
The search condition of the command (the
WHEREclause) is re-evaluated to see if the updated version of the row still matches the search condition.
待っている間に行の内容が変わっているので、WHERE をもう一度評価します。更新後の版がまだ条件に一致するかを確かめる手順です。
読み取り側の見え方も、公式が単位をはっきり書いています。
In effect, a
SELECTquery sees a snapshot of the database as of the instant the query begins to run.
単位はクエリが走り始めた瞬間です。トランザクション単位ではありません。だから次が成り立ちます。
Also note that two successive
SELECTcommands can see different data, even though they are within a single transaction, if other transactions commit changes after the firstSELECTstarts and before the secondSELECTstarts.
読み取り vs 書き込み 待たない(MVCCの保証)書き込み vs 書き込み 待つ(would-be updater が待つ)待った後 WHERE を re-evaluate するSELECT の見える範囲 クエリ開始の瞬間のスナップショット同一トランザクション内 2回の SELECT で違うデータが見えうる素の SELECT を止める ACCESS EXCLUSIVE のみ
ここで述べた挙動は Read Committed のものです。公式は “Read Committed is the default isolation level in PostgreSQL.” と書いており、これが既定値です。他の分離レベルでどう変わるかはこの期の後半に置いてあります。
同じ行を2つのトランザクションが同時に更新しようとしたとき、後から来た側はどうなりますか(Read Committed)。
読み取りが止まる唯一の条件
素の `SELECT` は普通は止まりません。しかし公式は “Only an `ACCESS EXCLUSIVE` lock blocks a `SELECT` (without `FOR UPDATE/SHARE`) statement.” と1つだけ例外を書いています。そのモードは “Conflicts with locks of all modes” で、保持者が “the only transaction accessing the table in any way” になります。取るコマンドも列挙されており(多くの形の `ALTER TABLE` を含む)、当てはまるかどうかは手順書を見れば判定できます。
止めるモード ACCESS EXCLUSIVE(これだけ)対象 SELECT (without FOR UPDATE/SHARE)性質 Conflicts with locks of all modes取るコマンド DROP TABLE / TRUNCATE / REINDEXCLUSTER / VACUUM FULLREFRESH MATERIALIZED VIEW(without CONCURRENTLY)
書き込み同士は待つ
*reading never blocks writing* が成り立っていても、書き込み同士は別です。公式は “the would-be updater will wait for the first updating transaction to commit or roll back (if it is still in progress)” と書いています。待った後は “The search condition of the command (the `WHERE` clause) is re-evaluated” となり、条件の再評価が入ります。保証の対象は読み書きの組み合わせであって、書き込みの組み合わせではありません。
先に更新した側 進む後から来た側 wait for ... commit or roll back待ちの終わり 相手の commit または roll back再開後 WHERE を re-evaluate保証の対象外 書き込み同士の競合
「素の SELECT が止まる唯一の条件」と「書き込み同士は待つ」。この2つは、覚え方のどんな失敗を同じように突いているでしょうか。
どちらも、前のレッスンの強い一文を「範囲を落として」覚えたときに間違える場所です。落ちている範囲が何かを考えてみてください。
この章のまとめ
- 「読み取りはブロックされない」は言い過ぎ。公式は Only an
ACCESS EXCLUSIVElock blocks aSELECT(withoutFOR UPDATE/SHARE) statement. と例外を1つ書いている。 - そのモードは Conflicts with locks of all modes で、保持者が the only transaction accessing the table in any way になる。だから素の
SELECTも入れない。 - 取るコマンドは列挙済み——
DROP TABLE/TRUNCATE/REINDEX/CLUSTER/VACUUM FULL/REFRESH MATERIALIZED VIEW(CONCURRENTLYなし)。加えて多くの形のALTER INDEX/ALTER TABLE。 - 書き込み同士は待つ。 would-be updater will wait for the first updating transaction to commit or roll back。待った後は
WHEREを re-evaluate する。 SELECTが見るのは クエリ開始の瞬間のスナップショット。だから同一トランザクション内の2回のSELECTで違うデータが見えうる。これは既定の Read Committed の挙動。- 逆向きに振り切って「MVCC は当てにならない」とするのも誤り。保証と例外を同じ精度で覚えるのが正解。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
「PostgreSQL では読み取りがブロックされないので、テーブルに対する運用作業中でも参照系の処理は必ず動き続ける」。この説明を評価してください。
用語集(4語)
- ACCESS EXCLUSIVE
- テーブルレベルのロックモードの1つ。公式は “Conflicts with locks of all modes” とし、“This mode guarantees that the holder is the only transaction accessing the table in any way.” と書く。素の `SELECT` を止める唯一のモード。
- 素の SELECT(without `FOR UPDATE/SHARE`)
- 公式が例外を述べる一文の対象。“Only an `ACCESS EXCLUSIVE` lock blocks a `SELECT` (without `FOR UPDATE/SHARE`) statement.” と書かれており、`FOR UPDATE` / `FOR SHARE` を付けたものはこの一文の対象に含まれない。
- would-be updater
- 公式が使う語で、これから更新しようとしている側のトランザクション。同じ行を先に更新した側がいる場合 “will wait for the first updating transaction to commit or roll back (if it is still in progress)” となる。
- Read Committed
- 公式が “_Read Committed_ is the default isolation level in PostgreSQL.” と書く既定の分離レベル。このレベルでは “a `SELECT` query sees a snapshot of the database as of the instant the query begins to run.”