トランザクションが同時に発生する可能性のある問題とその解決策




一、複数のトランザクションが同時に発生する可能性のある問題

  • Lost Update更新喪失a.第1類更新喪失、ロールバック上書き:1つのトランザクションを元に戻すと、そのトランザクション内の書き込み操作がロールバックされ、他のコミットされたトランザクションが書き込まれたデータが上書きされます.b.第2類の更新が失われ、オーバーライドがコミットされる:1つのトランザクションをコミットする時、書き込み操作はトランザクション内で読み取ったデータに依存し、読み取りは他のトランザクションがコミットされる前に発生し、書き込みは他のトランザクションがコミットされた後に発生し、他のコミットされたトランザクションが書き込まれたデータをオーバーライドした.これは再読不可の特例である.
  • Dirty Readダーティ・リード:1つのトランザクションが別のコミットされていないトランザクションに書かれたデータを読みました.
  • Non-Repeatable Readは繰り返してはいけません.1つのトランザクションで同じローのデータを2回読みますが、この2回のデータは違います.
  • Phantom Read幻読:1つのトランザクションで2回のクエリーがありますが、2回目のクエリーは1回目のクエリーより数行または数列のデータが多いか少ないです.

  • 2つの更新が失われた例


    時間
    引き出し取引A
    振込事務B
    T1
    トランザクションの開始
     
    T2
     
    トランザクションの開始
    T3
    リード残高1000
     
    T4
    100を取り出し、残高を900に変更
    -
    T5
     
    リード残高1000
    T6
     
    送金100、残高を1100に変更
    T7
     
    トランザクションのコミット、残高は1100
    T8
    取引を取り消し、残高を1000に戻す
    -
    T9
    最終残高1000、更新損失
    -
    書き込み操作に「継続-Xロック」を付けず、トランザクションBの書き込みを阻止できず、ロールバックオーバーライドが発生しました.
    時間
    振込事務A
    引き出し取引B
    T1
    トランザクションの開始
     
    T2
     
    トランザクションの開始
    T3
    リード残高1000
     
    T4
     
    リード残高1000
    T5
     
    100を取り出し、残高を900に変更
    T6
     
    トランザクションのコミット、残高900
    T7
    送金100、残高を1100に変更
    -
    T8
    トランザクションのコミット、残高は1100
    -
    T9
    最終残高1100、更新損失
    -
    書き込み操作には「継続-Xロック」、読み取り操作には「一時-Sロック」が加わり、トランザクションBの書き込みを阻止できず、コミットオーバーライドが発生した.

    二、取引隔離レベル


    複数のトランザクションが同時に発生する問題を解決するために、同時制御を行います.データベース・システムには、ユーザーが選択できる4つのトランザクション・独立性レベルがあります.
  • Read Uncommitted読み取り未送信:第1クラスの更新が失われることは許可されません.汚れた読み取りを許可し、トランザクションを隔離しません.
  • Read Committed読み取りがコミットされました:汚い読み取りは許可されず、繰り返し読み取りは許可されません.
  • Repeatable Readリピート可能:リピート不可.しかし、幻読が現れる可能性があります.
  • Serializableシリアル化:すべての追加削除チェックシリアル実行.

  • 読み取りコミットなし
    トランザクションの読み取りは他のトランザクションの読み取りと書き込みをブロックしません.トランザクションの書き込みは他のトランザクションの書き込みをブロックしますが、読み取りをブロックしません.書き込み操作に「継続-Xロック」を加えることで実現できます.

    読み取りコミット


    トランザクションの読み取りは、他のトランザクションの読み取りと書き込みをブロックしません.トランザクションの書き込みは、他のトランザクションの読み取りと書き込みをブロックします.書き込み操作に「継続-X」ロック、読み取り操作に「一時-Sロック」を加えることで実現できます.

    リピート可能


    トランザクションの読み取りは、他のトランザクションの書き込みをブロックしますが、読み取りをブロックしません.トランザクションの書き込みは、他のトランザクションの読み取りと書き込みをブロックします.書き込み操作に「継続-X」ロック、読み取り操作に「継続-Sロック」を付けることで実現できます.

    シリアル化


    「行レベルロック」ができない場合は、「表レベルロック」を使用します.
    シリアル化可能
    パラレルスケジューリングの結果がシリアルスケジューリングの結果と等価である場合、このパラレルスケジューリングはシリアル化可能である.
    トランザクション・独立性レベルを区別するのは、ダーティ・リード、重複不可リード、幻リードの3つの問題を解決するためです.
    トランザクション独立性レベル
    ロールバックオーバーライド
    汚読
    繰り返し不可
    コミットオーバーライド
    まぼろし読み
    読み取りコミットなし
    x
    起こり得る
    起こり得る
    起こり得る
    起こり得る
    読み取りコミット
    x
    x
    起こり得る
    起こり得る
    起こり得る
    リピート可能
    x
    x
    x
    x
    起こり得る
    シリアル化
    x
    x
    x
    x
    x

    三、よく使う解決方案


    ここに羅列された技術の中には、データベースシステムが実装されているものもあれば、開発者が自主的に完成する必要があるものもあります.

    1.バージョンチェック


    データベースに「バージョン」フィールドを保持し、データの読み書きに従ってデータのバージョンを判断します.バージョンは、タイムスタンプまたはステータスフィールドのいずれかです.
    次の例のWHERE句では、簡単なバージョンチェックが実現されます.
    UPDATE table SET status = 1 WHERE id=1 AND status = 0;

    バージョンチェックは「楽観的なロック」として機能し、更新が失われた問題を解決します.

    2.ロック


    2.1共有ロックと排他ロック
    共有ロック(Shared locks,S-locks)
    基本ロックタイプの1つです.共有ロックを追加したオブジェクトは、現在のトランザクションと他のトランザクションでのみ読み込むことができます.リードロックとも呼ばれます.ロックなし、Sロックを追加したオブジェクトにSロックを追加できます.オブジェクトは複数のSロックの追加を受け入れることができます.
    排他ロック(Exclusive locks,X-locks)
    基本ロックタイプの1つです.ロックされたオブジェクトを並べ替えると、現在のトランザクションでのみ読み取りと書き込みが許可されます.パーソナルロック、ライトロックとも呼ばれます.ロックされていないオブジェクトにのみXロックを追加できます.オブジェクトはXロックを1つしか受けられません.X錠をかけたオブジェクトには、これ以上鍵をかけることはできません.
    更新ロック(Update locks,U-locks)
    ロックタイプの1つです.これは、多くのデータベースがXロックを実装する際に、Sロックを先に追加し、追加に成功した後、Xロックに交換しようとするプロセスを実行しているためです.このとき,2つのトランザクションが同時にSロックをかけ,Xロックを交換しようとするとデッドロックが発生する.そのため、Uロックを追加すると、Uロックは更新の意向があり、1つのトランザクションだけがUロックを取得することを許可します.このトランザクションは、書き込みが発生した後、UロックがXロックに変わり、書き込みがない場合はSロックと見なされます.
    今のところMSSQLでしかUロックが見られないようです.
    2.2一時ロックと持続ロック
    ロックの時効性.現在の文が終了するか、現在のトランザクションが終了するかを指定します.
    2.3表レベルロックと行レベルロック
    ロックの粒度.ロックされたオブジェクトが現在のテーブルであるか、現在のローであるかを示します.
    ここでMSSQLの「ロック粒度と階層」について学びます.
    2.4悲観ロックと楽観ロック
    この2つのロックの言い方は、主に「本当にデータベースレベルでロックをかけるかどうか」について議論しています.
    悲観ロック(Pessimistic Locking)
    悲観的ロック現在のトランザクションがデータ・リソースを操作していると仮定すると、他のトランザクションが同時にデータ・リソースにアクセスし、現在のトランザクションの操作が干渉されないように、リソースをロックします.悲観的なロックは、行レベルの排他的ロックや表レベルの排他的ロックなど、データベースのロックメカニズムを使用して実現する必要があります.
    悲観的なロックは、更新の損失や重複しない問題を防止しますが、同時性に非常に影響するため、慎重に使用する必要があります.
    楽観ロック
    楽観ロックは、現在のトランザクションがデータ・リソースを操作している場合、他のトランザクションが同時にアクセスしないため、データベース階層でロックされていないと仮定します.楽観的なロックは、プログラムロジックによって制御される技術を使用して、発生する可能性のある同時問題を回避します.
    高同時性と高伸縮性を同時に維持できる唯一の方法は、バージョンチェック付きの楽観的なロックを使用することです.
    楽観的なロックでは、ダーティ・リードの問題は解決されません.そのため、データベースが少なくともコミットされたトランザクション・独立性レベルを有効にする必要があります.

    3.三級ロックプロトコル


    プロトコルとは、それを使用するときに、すべてのトランザクションがルールに従わなければならないことを意味します!!!
    一級ロックプロトコル
    トランザクションは、データを変更する前に、トランザクションが終了(コミットまたは終了)するまでXロックを追加する必要があります.データを読むだけなら、ロックをかける必要はありません.
    次の例を示します.
    SELECT xxx FOR UPDATE;
    UPDATE xxx;

    二次ロックプロトコル
    一級のロックプロトコルを満たし、トランザクションはデータを読み込む前にSロックを追加しなければならず、読み終わったらSロックを解放することができる.
    トリプルロックプロトコル
    一級のロックプロトコルを満たし、トランザクションはデータを読み込む前にSロックを追加し、トランザクションが終了するまで解放しなければならない.

    4.2セグメントロックプロトコル(2-phase locking)


    ロックフェーズ:トランザクションはデータを読む前にSロックを追加し、データを書く前にXロックを追加し、ロックが成功しない場合は待機します.ロック解除フェーズ:ロックの解除が開始されると、ロックの追加は許可されません.
    同時に実行されるすべてのトランザクションが2つのロック・プロトコルに準拠している場合、これらのトランザクションの同時スケジューリング・ポリシーはシリアル化可能です.2つのセグメント・ロック・プロトコルに従うトランザクション・スケジューリング処理の結果は、シリアル化可能な十分な条件であるが、シリアル化可能は必ずしも2つのセグメント・ロック・プロトコルに従うわけではない.
    2つのロックプロトコルとデッドロック防止の1回のロック法の異同点
    1回のブラックアウト法では、各トランザクションが使用するすべてのデータを一度にロックする必要があります.そうしないと、実行を続行できません.そのため、1回のブラックアウト法は2つのロックプロトコルを遵守します.ただし、2つのロック・プロトコルでは、トランザクションが使用するすべてのデータを一度にロックする必要はありません.そのため、2つのロック・プロトコルに従うトランザクションでデッドロックが発生する可能性があります.

    四、異なる取引隔離レベルとそれに対応する選択可能なロックプロトコル


    トランザクション独立性レベル
    ロックプロトコル
    読み取りコミットなし
    一級ロックプロトコル
    読み取りコミット
    二次ロックプロトコル
    リピート可能
    トリプルロックプロトコル
    シリアル化
    2つのロックプロトコル
    ブラックアウト・プロトコルと独立性レベルは厳密に対応していません.
    トランザクション・アイソレーション・レベル-ロックの選択-3段階ロック・プロトコルのつながりを理解するには、多くの時間がかかります.