X(Twitter) Zenn GitHub RSS 共有

MySQLにおけるファントムリード

作成日時:2025-01-26
更新日時:2025-01-26

※あくまで自分の解釈。正しいとは限らない。

なぜREPEATABLE READでファントムリードが発生しないか

MVCCとギャップロックのおかげ。

基本的には、MVCCによりファントムリードは発生しない。
下記のパターン3などはMVCCだけでは不十分でロックが必要になる。

パターン1:

1. トランザクションA:SELECT * FROM tbl;
2. トランザクションB:行挿入してコミット。
3. トランザクションA:再度SELECT * FROM tbl;
   2で挿入したデータは見えない

ファントムリードは発生しない。
3のSELECTは1の時のスナップショットを参照しているから。

パターン2:

1. トランザクションA:SELECT * FROM tbl WHERE (インデックスを使用した範囲検索) FOR UPDATE;
2. トランザクションB:行挿入はギャップロックで失敗

登録できないのでファントムリードは発生しない。

パターン3:

1. トランザクションA:SELECT * FROM tbl;
2. トランザクションB:行挿入してコミット。
3. トランザクションA:SELECT * FROM tbl FOR UPDATE;
   2で挿入したデータが見えてしまう。
4. トランザクションA:SELECT * FROM tbl;

3でファントムリードが発生する。
4は1と同じ内容、つまり2で挿入したデータは見えない。
MySQL固有の仕様らしい。

パターン3に関して

15.7.2.4 読取りのロックに下記の記載がある。

一貫性読み取りでは、読み取られたビュー内に存在するレコードに設定されたロックはすべて無視されます。 (古いバージョンのレコードはロックできません。レコードのインメモリーコピー上の Undo ログに適用することで、再構築されます。)

どういう意味だ?(機械翻訳らしいからよくわからん、後で原文を読む)
以下、推察。

参考