InnoDBにおけるトランザクション分離レベルについて


InnoDBにおけるトランザクション分離レベルについて

InnoDB

  • すべてのデータは主キーのB+Treeのリーフノードに格納されている
    • この構造をクラスタリングインデックスという
  • セカンダリインデックスはリーフノードには主キーの値がついている
    • つまりセカンダリインデックスでの検索は、まず主キーを割り出してから主キーで検索をかけ直す、という流れでデータを取得してくる
    • 速度としては主キー検索の二倍程度になる
  • インデックスのリーフノードについている値だけでクエリを完遂できる場合、使用されるインデックスをカバリングインデックスと呼ぶ
    • セカンダリインデックスで主キーの値を取る場合、カバリングインデックスとなり速度が1/2になる
  • バッファプール、ログバッファ、ログ、データベースストレージで構成される。
    • コミットのタイミングではログに書き込みがされるだけ
    • ログからストレージへは非同期。

トランザクションについて

課題となる参照不整合

  • ファントムリード
    • 挿入、削除による不整合
    • 検索タイミングによって、抽出レコードが変わる
  • ファジーリード 
    • 更新による不整合
    • 検索タイミングによって、抽出したレコードの内容が変わる

ロックについて

  • 共有ロック
    • 読み取り用ロック。更新を禁止し、排他ロックを防ぐことができるので、ロックを取得している間は一貫した値が取得できることを保証できる。
  • 排他ロック
    • 更新用ロック。更新、読み取りを禁止する。

MVCC

  • READ COMMITEDとREPEATABLE READで有効
  • レコードには作成TrxID、更新TrxID、UNDOログへのポインタが仕込まれている。
    • 古いTrxIDのトランザクションからは未来のTrxIDによる更新、作成のものは見えない => UNDOから遡るか、完全に見えない(新規レコードの場合)
    • クエリが発行されてはじめてIDが振られる。BEGINしただけでは振られない。
    • ある行Aに対してUNDOログ参照してる古いトランザクションでも、更新時にAのロックさえ取れれば古い値で更新みたいなことができる = ロストアップデートが起きる

分離レベル

  • SERIAZIABLE
    • 要件: ファジーリードもファントムリードも起こらない。取得するデータはすべて最新のものである。
      • 参照されるものはすべてのトランザクションにおいて最新のものである
    • selectで対応する行すべてに共有ロックをかけるためファントムもファジーリードも起こらない
    • ネクストキーロックによるファントムリードの回避
      • インデックスレコードに対するロックとギャップのロック(対象行の範囲の前と後に何も入れられない)
  • REPEATABLE-READ
    • 要件: ファジーリードを起こらないが、ファントムリードは発生しうる。
      • 参照されるものは同一トランザクションでは常に同じ。
    • InnoDBにおいてはMVCCによってどちらも回避できる。
    • ただ、ノンロッキングリード => ロッキングリードに切り替わった場合は、参照先がロールバック・セグメントから最新データに切り替わるので要件を満たせない。
    • ノンロッキングリード時はMVCCによって同じトランザクション内では同じバージョンのロールバックセグメントを参照する。
  • READ COMMITTED
    • 要件: ファジーリードもファントムリードも発生しうる。
    • selectした時にその行のそのときのバージョンのスナップショットを参照する
  • READ UNCOMMITTED
    • 要件: コミットされていない更新レコード、新規レコードも参照できる
      • コミットされていないもの含めてすべての最新値を取得する

READ COMMITTEDとREPEATABLE-READのMVCCの違い

  • READ COMMITTEDの場合は、同一トランザクション内でも参照するスナップショットのバージョンが異なるケースがあるらしい
  • ロッキングリードの挙動は、READ-COMMITEDの場合は行ロック、REPEATABLE-READの場合はネクストキーロック