- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け
- Nginx受け口
- アプリ処理
- DB保存今ここ
このコースの前半では、PostgreSQL の MVCC(古い行版を残す仕組み)と掃除・WAL を見てきました。ここからは分離レベルの既定値を、MySQL 8.4 の InnoDB と PostgreSQL 18 で比べながら積み上げます。インデックスはまだ扱っていません。
まず結論
同じSQLを書いても、**InnoDB は REPEATABLE READ、PostgreSQL は READ COMMITTED** で走ります。分離レベルは「自分が選ぶもの」だと思われがちですが、多くの現場では**誰も選んでいない既定値**のまま動いています。そしてその既定値が、製品によって逆側にずれています。
さらに MySQL では **autocommit が既定で ON** です。つまり `BEGIN` と書かない限り1文ごとに別のトランザクションになり、**「REPEATABLE READ だから何度読んでも同じ」は成立しません**。レベル名だけを見て安心するのが、ここで最初に踏む地雷です。
MySQL(InnoDB)と PostgreSQL で、トランザクション分離レベルの既定値はどうなっているでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
誰も選んでいない設定が、挙動を決めている
トランザクション分離レベルは「要件に応じて選ぶもの」として説明されます。しかし実際の現場では、選んだ覚えがない値のまま動いていることがほとんどです。厄介なのは、その既定値が製品ごとに揃っていない点です。
MySQL 8.4 のマニュアルはこう書いています。
The default isolation level for
InnoDBisREPEATABLE READ.
PostgreSQL 18 の文書はこうです。
Read Committed is the default isolation level in PostgreSQL.
片方は REPEATABLE READ、もう片方は READ COMMITTED。 4段階のうち隣り合う2つが、それぞれの既定になっています。「DBの既定はだいたい READ COMMITTED だろう」という感覚で MySQL を触ると逆側に外します。思い込みが片方の製品では正しいのが、事故の温床です。
表記もここで押さえます。MySQL の起動オプション表は、こう記載されています。
Command-Line Format:
--transaction-isolation=name/ Type: Enumeration / Default Value:REPEATABLE-READ/ Valid Values:READ-UNCOMMITTED,READ-COMMITTED,REPEATABLE-READ,SERIALIZABLE
設定値は REPEATABLE-READ とダッシュ区切り、本文がレベルを指すときは REPEATABLE READ とスペース区切りです。設定ファイルに書くときは前者、文章で語るときは後者になります。
設定ファイルに書く値と、分離レベルの名前の表記について、正しいのはどれですか。
RR と RC の違いは「スナップショットを固定するか」
レベル名から意味を推測すると外します。InnoDB の説明は機構の言葉で書かれています。
If the transaction isolation level is
REPEATABLE READ(the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction.
With
READ COMMITTEDisolation level, each consistent read within a transaction sets and reads its own fresh snapshot.
違いは一点です。最初の読み取りで決めた一時点を読み続けるか、読むたびに取り直すか。 前者なら同じトランザクション内で2回読んでも同じ、後者なら他のトランザクションのコミットが見えます。
ただし、適用範囲に限定が付いています。
The snapshot of the database state applies to
SELECTstatements within a transaction, not necessarily to DML statements.
固定されるのは SELECT の見え方です。UPDATE や DELETE は「必ずしも」そのスナップショットに従いません。「トランザクション全体が過去の一時点を見ている」と丸めてはいけないのは、この一文があるからです。
BEGIN を書かないと、レベルは仕事をしない
ここが実務でいちばん効く点です。MySQL の既定は、分離レベルだけではありません。
By default, MySQL starts the session for each new connection with
autocommitenabled, so MySQL does a commit after each SQL statement if that statement did not return an error.
If
autocommitmode is enabled, each SQL statement forms a single transaction on its own.
1文が1トランザクションになります。先の定義の前提——“within the same transaction”——が成立しません。BEGIN なしで同じ SELECT を2回実行したとき、それは同じトランザクションの2回目ではなく、別々のトランザクションの1回目が2つです。毎回新しいスナップショットになります。
BEGIN なし 文1 = トランザクションA(新しいスナップショット)文2 = トランザクションB(また新しいスナップショット)→ 途中の他者のCOMMITが見えるBEGIN あり 文1 = トランザクションAの1回目 ← ここでスナップショット確定文2 = トランザクションAの2回目(同じスナップショット)→ 途中の他者のCOMMITは見えない
「REPEATABLE READ だから一貫して読める」は、BEGIN があって初めて成立します。 抜け道は用意されています。
A session that has
autocommitenabled can perform a multiple-statement transaction by starting it with an explicitSTART TRANSACTIONorBEGINstatement and ending it with aCOMMITorROLLBACKstatement.
If
autocommitmode is disabled within a session withSET autocommit = 0, the session always has a transaction open.
明示的に始めるか、autocommit を切るか。どちらも取らなければ、レベル名は看板だけになります。
MySQL で `BEGIN` を書かずに同じ `SELECT` を2回実行しました。既定の REPEATABLE READ なので2回とも同じ結果になる、と言えますか。
製品をまたぐと既定値が逆になる
InnoDB は REPEATABLE READ、PostgreSQL は READ COMMITTED。同じSQLを同じ順で書いても、他のトランザクションのコミットが見えるかどうかが変わります。移行やマルチDB構成で「コードは同じなのに挙動が違う」となる理由の一つがここです。
MySQL 8.4 / InnoDB分離レベル REPEATABLE READ設定値表記 REPEATABLE-READautocommit ONPostgreSQL 18分離レベル READ COMMITTED内部実装 4段階を要求できるが、実装は3種類
同じ製品の中で、2つの既定値が噛み合う
InnoDB の既定は REPEATABLE READ ですが、autocommit も既定 ON です。レベルとしては一貫読み取りができるのに、トランザクションが1文で閉じるためその場面が現れません。「レベルを確認したから大丈夫」で止まると見落とします。
mysql> SELECT @@transaction_isolation;mysql> SELECT @@autocommit;postgres=# SHOW transaction_isolation;→ レベルと autocommit の両方を見ないと実際の挙動は決まらない
「製品をまたぐと既定値が逆になる」ことと「同じ製品で2つの既定値が噛み合う」こと。この2つに共通している、確かめ方の教訓は何でしょうか。
どちらも「自分が設定した覚えのない値」が挙動を決めています。ではどこを見れば分かるでしょうか。
この章のまとめ
- InnoDB の既定は
REPEATABLE READ、PostgreSQL の既定はREAD COMMITTED。 隣り合う2レベルが逆側の既定になっている。 - 設定値はダッシュ区切り
REPEATABLE-READ、レベル名はスペース区切りREPEATABLE READ。 - RR と RC の差はスナップショットを固定するか取り直すか。固定されるのは
SELECTで、DML には “not necessarily” と限定が付く。 - autocommit は既定 ON。
BEGINを書かない限り RR の一貫読み取りは成立しない。 - 4レベルの対照表は PostgreSQL 18 文書の Table 13.1 由来。MySQL 側に同等の表は見当たらなかった。
- 確かめ先は設定ファイルではなく動いているサーバ。レベルと autocommit を両方見る。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
REPEATABLE READ と READ COMMITTED の、スナップショットに関する本質的な違いはどれですか。
用語集(4語)
- REPEATABLE READ(InnoDB の既定)
- MySQL 8.4 の記述は “The default isolation level for `InnoDB` is `REPEATABLE READ`.”。一貫読み取りの挙動は “all consistent reads within the same transaction read the snapshot established by the first such read in that transaction”。設定値として書くときは `REPEATABLE-READ` とダッシュ区切りになる。
- READ COMMITTED(PostgreSQL の既定)
- PostgreSQL 18 の記述は “_Read Committed_ is the default isolation level in PostgreSQL.”。InnoDB でこのレベルを使った場合は “each consistent read within a transaction sets and reads its own fresh snapshot” となり、同じトランザクション内でも読み直すたびに最新のスナップショットになる。
- autocommit
- MySQL 8.4 のシステム変数で Default Value は `ON`。“If `autocommit` mode is enabled, each SQL statement forms a single transaction on its own.” 明示的に `START TRANSACTION` か `BEGIN` を書くか、`SET autocommit = 0` にしない限り、1文ごとにコミットされる。
- consistent read(一貫読み取り)
- スナップショットを読む読み取り。スナップショットの適用範囲には限定があり、“The snapshot of the database state applies to `SELECT` statements within a transaction, **not necessarily to DML statements**.” と書かれている。