REPEATABLE READの話

MySQL5系で、REPEATABLE READな環境があったのですが、ん?と思うことがあったので、備忘録。 結論から言うと、MySQL5.5 + InnoDBでファジーリードやファントムリードと思われる挙動が確認できた(気がする)。

InnoDBのREPEATABLE READでハマった話 - カイワレの大冒険 Second

これ、厳密にはファジーリードとは言えないのですよ。ちょっと見ていきましょうか。

まず、複数トランザクションが同一の行をupdateした場合の挙動は、公式リファレンスマニュアルに書いてあります。

テーブル内のいくつかの行を更新すると、SELECT は他の行の古いバージョンを確認すると同時に、更新された行の最新のバージョンを確認します。もし別のユーザが同時に同じテーブルを更新すると、今までとは違う状態のテーブルをデータベース内で確認するかもしれないいう例外があるかもしれません。

MySQL :: MySQL 5.1 リファレンスマニュアル :: 13.5.10.4 一貫非ロック読み取り

つまり自分で更新した行をselectする場合は最新の状態を見る、すなわち他のコミットの影響を受けるとしれっと書いてあります。実際、この挙動はほとんどのユースケースでは有用なのであまり問題にはなりませんが、果たしてこれをREPEATABLE READと呼べるのでしょうか?

そもそもREPEATABLE READとは何なのでしょうか。これはSQL92で定義されていますが、そこにはノンリピータブルリードが発生しない、としか書かれていません。

ノンリピータブルリード(ファジーリード)は、というと

SQL-transaction T1 reads a row. SQL-transaction T2 then modifies or deletes that row and performs a COMMIT. If T1 then attempts to reread the row, it may receive the modified value or discover that the row has been deleted.

としか書かれていません。これ以外のケースは仕様の外。T2で行の更新がコミットされた後、T1で同じ行が更新されるとき、T2のコミットをどう扱うかといったケースは定義されていません。参照元ブログのケースも、ここで定義されたノンリピータブルリードは発生していないので、REPEATABLE READは成立しています。

また、ファントムリードは

SQL-transaction T1 reads the set of rows N that satisfy some . SQL-transaction T2 then executes SQL-statements that generate one or more rows that satisfy the used by SQL-transaction T1. If SQL-transaction T1 then repeats the initial read with the same , it obtains a different collection of rows.

同じ条件でreadした時に別トランザクションでinsertした行が見えてしまう事、とあるので、元記事の状態はファントムリードではありません。

REPEATABLE READにおけるupdateの挙動は実装依存のようです。各DBで複数トランザクションから同一行をupdateした場合の挙動は、

となっています。

そういえば、多くのDB実装ではREAD UNCOMMITEDがデフォルトになっていますが、MySQLはREPEATABLE READとなっています。これはselectした行でロックしないため性能劣化を気にしなくてよいといった事情があるのかもしれません。