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

既定値が違う2つのDB — 同じSQLが、同じようには走らない

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力
  2. DNS名前→IP
  3. ルータ経路選択
  4. ロードバランサ振り分け
  5. Nginx受け口
  6. アプリ処理
  7. 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 InnoDB is REPEATABLE 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 COMMITTED isolation level, each consistent read within a transaction sets and reads its own fresh snapshot.

違いは一点です。最初の読み取りで決めた一時点を読み続けるか、読むたびに取り直すか。 前者なら同じトランザクション内で2回読んでも同じ、後者なら他のトランザクションのコミットが見えます。

ただし、適用範囲に限定が付いています。

The snapshot of the database state applies to SELECT statements within a transaction, not necessarily to DML statements.

固定されるのは SELECT の見え方です。UPDATE や DELETE は「必ずしも」そのスナップショットに従いません。「トランザクション全体が過去の一時点を見ている」と丸めてはいけないのは、この一文があるからです。

具体実物

BEGIN を書かないと、レベルは仕事をしない

ここが実務でいちばん効く点です。MySQL の既定は、分離レベルだけではありません。

By default, MySQL starts the session for each new connection with autocommit enabled, so MySQL does a commit after each SQL statement if that statement did not return an error.

If autocommit mode is enabled, each SQL statement forms a single transaction on its own.

1文が1トランザクションになります。先の定義の前提——“within the same transaction”——が成立しません。BEGIN なしで同じ SELECT を2回実行したとき、それは同じトランザクションの2回目ではなく、別々のトランザクションの1回目が2つです。毎回新しいスナップショットになります。

既定のまま同じSELECTを2回 — BEGINの有無で前提が変わる表
BEGIN なし 文1 = トランザクションA(新しいスナップショット)
文2 = トランザクションB(また新しいスナップショット)
→ 途中の他者のCOMMITが見える
 
BEGIN あり 文1 = トランザクションAの1回目 ← ここでスナップショット確定
文2 = トランザクションAの2回目(同じスナップショット)
→ 途中の他者のCOMMITは見えない

「REPEATABLE READ だから一貫して読める」は、BEGIN があって初めて成立します。 抜け道は用意されています。

A session that has autocommit enabled can perform a multiple-statement transaction by starting it with an explicit START TRANSACTION or BEGIN statement and ending it with a COMMIT or ROLLBACK statement.

If autocommit mode is disabled within a session with SET autocommit = 0, the session always has a transaction open.

明示的に始めるか、autocommit を切るか。どちらも取らなければ、レベル名は看板だけになります。

✓ ここまでの確認

MySQL で `BEGIN` を書かずに同じ `SELECT` を2回実行しました。既定の REPEATABLE READ なので2回とも同じ結果になる、と言えますか。

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

製品をまたぐと既定値が逆になる

InnoDB は REPEATABLE READ、PostgreSQL は READ COMMITTED。同じSQLを同じ順で書いても、他のトランザクションのコミットが見えるかどうかが変わります。移行やマルチDB構成で「コードは同じなのに挙動が違う」となる理由の一つがここです。

既定値の対照(それぞれのマニュアル記載)設定ファイル
MySQL 8.4 / InnoDB
分離レベル REPEATABLE READ
設定値表記 REPEATABLE-READ
autocommit ON
 
PostgreSQL 18
分離レベル READ COMMITTED
内部実装 4段階を要求できるが、実装は3種類
具体例 B

同じ製品の中で、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 を両方見る。

自分の現場で確かめる

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

環境:

理解度チェック

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

問題 1 / 3正解: 0

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**.” と書かれている。