Dev Study
← データベースの仕組み
レッスン 2 / 13·約 9 分·第6期 データベースの仕組み

その保証が効かないとき — 素のSELECTが止まる唯一の条件

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力
  2. DNS名前→IP
  3. ルータ経路選択
  4. ロードバランサ振り分け
  5. Nginx受け口
  6. アプリ処理
  7. 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 EXCLUSIVE lock blocks a SELECT (without FOR 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 WHERE clause) is re-evaluated to see if the updated version of the row still matches the search condition.

待っている間に行の内容が変わっているので、WHERE をもう一度評価します。更新後の版がまだ条件に一致するかを確かめる手順です。

読み取り側の見え方も、公式が単位をはっきり書いています。

In effect, a SELECT query sees a snapshot of the database as of the instant the query begins to run.

単位はクエリが走り始めた瞬間です。トランザクション単位ではありません。だから次が成り立ちます。

Also note that two successive SELECT commands can see different data, even though they are within a single transaction, if other transactions commit changes after the first SELECT starts and before the second SELECT starts.

待つ・待たないの整理(Read Committed)表
読み取り 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)。

⇄ 2つを見比べる
具体例 A

読み取りが止まる唯一の条件

素の `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 / REINDEX
CLUSTER / VACUUM FULL
REFRESH MATERIALIZED VIEW
(without CONCURRENTLY)
具体例 B

書き込み同士は待つ

*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” となり、条件の再評価が入ります。保証の対象は読み書きの組み合わせであって、書き込みの組み合わせではありません。

同じ行を2つが更新しようとしたとき表
先に更新した側 進む
後から来た側 wait for ... commit or roll back
待ちの終わり 相手の commit または roll back
再開後 WHERE を re-evaluate
保証の対象外 書き込み同士の競合

「素の SELECT が止まる唯一の条件」と「書き込み同士は待つ」。この2つは、覚え方のどんな失敗を同じように突いているでしょうか。

どちらも、前のレッスンの強い一文を「範囲を落として」覚えたときに間違える場所です。落ちている範囲が何かを考えてみてください。

この章のまとめ

  • 「読み取りはブロックされない」は言い過ぎ。公式は 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 になる。だから素の 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 は当てにならない」とするのも誤り。保証と例外を同じ精度で覚えるのが正解。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

環境:

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 3正解: 0

「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.”