まず、3つのロックコンテキストがあります。
- データベースレベルのロック
- テーブルレベルのロック
- 行レベルのロック
次に、4つのロックモードがあります。
IXおよびISロックは「意図的な」ロックです。これらのロックは、他のタイプのロックを取得する前に保持されます。Xロックは排他的(書き込み)ロックであり、Sロックは共有(読み取り)ロックです。
ロック(IX、IS、X、またはS)ロックは、任意のコンテキストレベルで取得できます。データベースレベルでのXロックは、たとえばデータベース内の他のすべての操作をブロックします。これはSQLliteが取るタイプのロックです。読み取り中にデータベース全体に対してSロックが取得され、書き込み中にデータベース全体に対してXロックが取得されます。書き込みは、Sロックが完了するのを待ち、書き込みロックが解放されるまで、新しいSロックとXロックをブロックします。これにより、シリアル化可能な分離トランザクションレベルが提供されます。
MySQLの場合、ロックはストレージエンジンに依存します。MyISAMは、テーブル全体(セット)に対してXおよびSロックを取得します。Xロックは、既存のSまたはXロックを待機し、新しいロックをブロックします。新しいXロックはキュー内でより高い優先順位が与えられ、新しいSロックよりも先に移動します。この動作は、LOW_PRIORITY_UPDATESを設定することで変更できます。これにより、書き込みの優先順位が下げられて読み取りが優先されるため、書き込みが不足する可能性があります。
MySQLでは、「FLUSH TABLESWITHREADLOCK」を使用してデータベース全体のXロックを取得することができます。
InnoDBは、インデックスの読み取りを介して行が検出されると、行をロックします。InnoDBはインデックスレコードをロックし、インデックスレコードがトラバースされるときにレコードをロックします。InnoDBは、「ギャップ」ロックと呼ばれる特別なロックを使用して、REPEATABLE-READトランザクション分離レベルを確保します。ロックはインデックスエントリで保持されるため、テーブルがUPDATEクエリに対して適切にインデックス付けされていない場合、多くの行がロックされます。InnoDBは、通常のSELECTクエリに対してSロックを作成しないことに注意してください。一貫性のあるスナップショットのために、行レベルのロックではなく、行のバージョン管理を使用します。
Xロックを取得する場合、データベースはデッドロックを検出する必要があります。次のことを考慮してください。
>connection 1
start transaction;
update T set c = c + 1 order by id asc;
>connection 2
start transaction;
update T set c = c - 1 order by id desc;
行ロックモデルでは、これら2つのステートメントの両方を正常に完了することはできません。最初のものは、2番目が保持するロックを取得するために永遠に待機し、その逆も同様です。データベースは、ロールバックする接続の1つを選択します。InnoDBは、変更の数が最も少ない接続を選択します。MyISAMは、最初にロックを取得した接続に対してテーブル全体をロックし、最初の接続が完了した後に2番目の接続が実行されます。
あなたが与えた簡単な例は、任意のコンテキスト(データベース、テーブル、または行)でのXロックによって解決されます。2つの接続がまったく同じタイプで始まり、両方が同じ行を更新しようとする2つの更新を実行している場合、両方がXロックを取得しようとします。Xロックを取得できる接続は1つだけです。どちらがロックを取得するかを正確に決定することはできません。他の接続は、Xロックを取得できるようになるまで、ロックが解放されるまで待機する必要があります。行がDELETEまたはUPDATEによってロックされた場合、データベースにロックするものが残っていないため、ウェイターは待機後にロックを取得しなくなる可能性があることに注意してください。
この例では、Xロックを取得する最初のUPDATEと2番目のUPDATEがXロックを待機し、最終的に実行されますが、どの行とも一致しません。