MySQLのトランザクションのアノマリーについて

概要

MySQLのトランザクションのアノマリーについてまとめる。 MySQLのバージョンは8系を想定する。

検証環境

検証に使う環境はdocker-composeで用意した。(1コンテナだけなのでcomposeを使わなくも良いのだが..)

docker-compose.yml

1_schema.sql

docker compose upでお手元にMySQL8系のコンテナが用意できる。

# トランザクション分離レベル MySQLのInnoDBでは、ANSI/ISO SQL標準で定められている4つのトランザクション分離レベルが提供されている。

分離レベル ダーティリード インコンシステントリード ロストアップデート ファントムリード
READ UNCOMMITTED
READ COMMMITED ×
REPEATABLE READ※1 × × ○※
SERIALIZABLE × × × ×

※1MySQLではREPEATABLE READがデフォルトとなっている。

※2上記では○になっているが、MySQLではREPEATABLE READにおいてファントムリードが発生しないようになっている。

トランザクションの分離レベルはREAD UNCOMMITTEDが一番低く、SERIALIZABLEが一番高い。上記は上から低い順となっている。基本的には分離性が高いほど性能が低下する傾向にある。

トランザクションについては、トランザクション概観にもまとめている。

アノマリー

トランザクションにおけるアノマリーについてMySQLで再現してみる。

アノマリーとは、「トランザクションの分離レベルや処理順序によって生じる期待しない結果や不整合」のこと。

アノマリーはANSI SQL標準やISO/IEC 9075によって定義されているものがあり、ここで取り上げるアノマリー以外にも色々ある。

インコンシステントリードについてはそれらの標準に定義されたものではない。(どこで定義されているのかはわからなかった。。.)

トランザクションはTXと表記する。複数トランザクションを区別するために数字を使う。(ex. TX1、TX2)

ダーティリード

ダーティリードは、TX1がTX2のCOMMIT前のデータを読み取ってしまう現象。

検証

すべてのセッションはREAD UNCOMMITEDで行う。

  1. TX1、TX2にてトランザクションを開始
  1. TX2にてデータ追加

TX2にてデータを追加、COMMITはしない。

  1. TX1にて再度データ読み取り

TX1でTX2のCOMMIT前のデータが読み取れてしまっている。

インコンシステントリード

インコンシステントリードは、読み取るデータに一貫性がない現象。

いろんなAnomaly#Inconsistent Read Anomalyを参照とした。

これについては正確な定義がちょっと分からなかったので、理解が正しいか怪しい。。

COMMIT後の一貫性のなさということなので、インコンシステントリードはファジーリードやファントムリードの上位概念??な感じがするが、厳密はおそらく違うはず・・。

検証

すべてのセッションはREAD UNCOMMITEDで行う。

  1. TX1にてトランザクション開始、データ読み取り
  1. TX2にてトランザクション開始、データ追加
  1. TX1にて再度読み取り

最初に読み取った結果と違う結果(TX2の処理結果)が取得され、一貫性がなくなっていることが確認できる。

ファジーリード(ノンリピータブルリード)

ファジーリードは、TX1が他のTX2にて更新したデータを参照できてしまう現象。

すべてのセッションはREAD COMMITTEDで行う。

検証

  1. TX1にてトランザクション開始、データ読み取り

初期データ投入結果。

  1. TX2にてトランザクション開始、データ読み取り

更新が完了。

  1. TX1にて再度データ読み取り

TX2のCOMMITが影響し、TX1の読み取り結果が変わったことが確認できる。

ファントムリード

ファントムリードは、TX2が新規追加または削除をCOMMITした場合にTX1が読み取るデータが変わってしまう現象。 ファジーリードは更新処理、ファントムリードは新規追加または削除が対象とした現象である。

検証

すべてのセッションはREAD COMMITTEDで行う。

  1. TX1にてトランザクション開始、データ読み取り
  1. TX2にてデータを追加、COMMIT

追加が完了。

  1. TX1にて再度データを取得

TX2のCOMMITが影響し、TX1の読み取り結果が変わったことが確認できる。

ロストアップデート

ロストアップデートは、TX1とTX2が同じデータを更新する際に競合が発生し、一部の更新が失われる現象。

検証

すべてのセッションはREPEATABLE READで行う。

  1. TX1でトランザクション開始、データ読み取り
  1. TX2にてトランザクション開始、データ読み取り
  1. TX1、TX2にそれぞれデータを更新
  1. TX1、TX2をそれぞれCOMMIT
  1. データ読み取り

TX1のCOMMITが失われてTX2にCOMMITが反映されていることが確認できる。

まとめ

トランザクションの分離レベルによって、発生するアノマリーは異なる。

アノマリーはCOMMIT前後でのデータの読み取りや一貫性が変わる現象としていくつかのパターンがある。

トランザクションのアノマリーについて詳しく学ぶにはトランザクションに関する本か何かを参照したほうが良さそう。

参考