MySQLテクニカルインサイダーInnoDBストレージエンジンスタディノート第6章ロック

ロックは、データベースシステムとファイルシステムを区別する重要な機能です。ロックメカニズムは、共有リソースへの同時アクセスを管理するために使用されます。

InnoDBエンジンは、テーブルデータをロックして、データの整合性と一貫性を提供します。さらに、データベース内の複数の場所でロックを使用して、LRUの追加、削除、変更など、さまざまなリソースへの同時アクセスを保証します。リスト。

異なるデータベースおよびエンジンで使用されるロックメカニズムの実装は、完全に異なる場合があります。MyISAMの場合、ロックはテーブルロックです。同時読み取りに問題はありませんが、同時挿入のパフォーマンスは低くなります。挿入が下部にある場合でも、MyISAMは特定の同時操作を実行できます。SQL Serverの場合、2005バージョンより前はすべてページロックでした。MyISAMのテーブルロックのパフォーマンスと比較して、テーブルロックのパフォーマンスが向上しています。2005バージョンでは、楽観的同時実行性と悲観的同時実行性がサポートされていました。楽観的同時実行性では、行レベルロックがサポートされるようになりました。MySQLは完全に異なります。

InnoDBエンジンロックの実装はOracleの実装と非常によく似ており、一貫した読み取りおよび行レベルのロックサポートを提供します。行レベルのロックには関連するオーバーヘッドがなく、同時実行性と一貫性を同時に実現できます。

InnoDBには、2つの行レベルのロックが実装されてい
ます。1。共有ロック(Sロック):トランザクションがデータの行を読み取ることができるようにします。
2.排他的ロック(Xロック):トランザクションがデータの行を削除または更新できるようにします。

ロックの互換性:トランザクションはすでに行rの共有ロックを取得しており、読み取りによって行rのデータが変更されないため、別のトランザクションが行rの共有ロックをすぐに取得できます。このとき、トランザクションが行rの排他ロックを取得する場合、トランザクションが行rの共有ロックを解放するのを待つ必要があります。この状況はロックの非互換性と呼ばれます。

InnoDBエンジンは複数粒度ロックをサポートしているため、行レベルのロックとテーブルレベルのロックを同時に存在させることができます。InnoDBは、インテントロックと呼ばれる追加のロック方法をサポートしています。インテントロックはテーブルレベルのロックです。設計の目的は、トランザクションが行のロックを保持しているか、ロックを保持する準備をしていることを示すことです。2つのインテントロックがあります。
1.意図的な共有ロック(ISロック):トランザクションは、テーブルの特定の行で共有ロックを取得しようとしています。
2.インテント排他ロック(IXロック):トランザクションは、テーブルの特定の行で排他ロックを取得しようとしています。

トランザクションがSロックとXロックを要求する前に、対応するIS、IXロック、およびインテンションロックを取得する必要があります。

現在のロック要求の情報を表示する:
ここに画像の説明を挿入
ここに画像の説明を挿入
InnoDBプラグインの前は、現在のデータベース要求SHOW FULL PROCESSLISTSHOW ENGINE INNODB STATUSwaitコマンドを介してのみ表示でき、現在のトランザクションのロック状況を判断できます。新しいバージョンでは、innodb_trx、innodb_locks、およびinnodb_lock_waitsの3つのテーブルがinformation_schemaアーキテクチャーの下に追加され、現在のトランザクションの監視と発生する可能性のあるロックの問題の分析が容易になりました。まず、innodb_trxテーブルフィールドを確認します
。1.trx_id:InnoDBエンジン内の一意のトランザクションID。
2.trx_state:現在のトランザクション状態。
3.trx_started:トランザクションの開始時刻。
4. trx_requested_lock_id:待機中のトランザクションのロックID。trx_stateの状態がLOCK WAITの場合、この値は、現在のトランザクションが待機する前にトランザクションが占有していたロックリソースのIDを表します。trx_stateがLOCKWAITでない場合、値はNULLです。
5.trx_wait_started:トランザクションが開始を待機する時間。
6.trx_weight:トランザクションによって変更およびロックされた行の数を反映した、トランザクションの重み。デッドロックをロールバックする必要がある場合、InnoDBエンジンはロールバックに最小値を選択します。
7.trx_mysql_thread_id:MySQLのスレッドID。
8.trx_query:トランザクションによって実行されるSQLステートメント。この値は、実際の使用ではNULLとして表示される場合があります。

innodb_trxテーブルは、現在実行中のトランザクションのみを表示でき、ロックステータスを判別できません。innodb_locksテーブルを使用して、ロックを表示します
。1.lock_id:ロックID。
2. lock_trx_id:トランザクションID。
3.lock_mode:ロックのモード。
4. lock_type:ロック、テーブルロック、または行ロックのタイプ。
5.lock_table:ロックされるテーブル。
6.lock_index:ロックのインデックス。
7.lock_space:テーブルスペースID。
8. lock_page:ロックされているページの数。テーブルがロックされている場合、この値はNULLです。
9.lock_rec:ロックされた行の数。テーブルがロックされている場合、この値はNULLです。
10.lock_data:ロックされた行の主キー値。テーブルがロックされている場合、この値はNULLです。この値は信頼できる値ではありません。範囲検索を実行する場合、lock_dataは最初の行の主キー値のみを返す場合があります。現在のリソースがロックされていて、ロックされたバッファープール内のページが置き換えられた場合、innodb_locksテーブルを表示すると、値はNULLとして表示されます。

各テーブルのロック状態を検出した後、ロック待機状態を判断できます。トランザクション量が多すぎると判断が容易ではありません。このとき、待機状態はinnodb_lock_waitsテーブルで確認できます。次のフィールド:
1。requesting_trx_id:ロックリソースを要求したトランザクションID。
2.requesting_lock_id:要求されたロックのID。
3. blockint_trx_id:現在このロックを占有しているトランザクションID。
4.blocking_lock_id:現在占有されているロックのID。

次の共同クエリを実行して、詳細情報を視覚的に確認できます。

SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query
FROM information_schema.innodb_lock_waits w 
INNER JOIN information_schema.innodb_trx b 
ON b.trx_id = w.blocking_trx_id 
INNER JOIN information_schema.innodb_trx r 
ON r.trx_id = w.requesting_trx_id;

INNER JOINはJOINと同じです。つまり、2つのテーブルの共通部分が選択されます。操作の結果は次のとおりです。
ここに画像の説明を挿入
ここに画像の説明を挿入
一貫性のある非ロック行読み取りとは、InnoDBが行マルチバージョン制御を介して現在の実行時間データベース内の行データを読み取ることを意味します。読み取り行がDELETE、UPDATE操作を実行している場合、この時点で読み取り操作は行ロックの解放を待機しませんが、行のスナップショットデータを読み取ります。
ここに画像の説明を挿入
これが非ロック読み取りと呼ばれる理由は次のとおりです。必要ないためアクセスした行のXロックが解除されるのを待ちます。スナップショットデータは、元に戻すセグメントを介して実装された前のバージョンの行のデータであり、元々元に戻すセグメントはトランザクションでデータをロールバックするために使用されたため、スナップショットデータ自体にオーバーヘッドはありません。履歴データを変更する必要がないため、スナップショットデータの読み取りをロックする必要はありません。

非ロック読み取りにより、データ読み取りの同時実行性が向上します。InnoDBエンジンのデフォルト設定では、これがデフォルトの読み取り方法ですが、トランザクション分離レベルが異なると、読み取り方法が異なり、すべてのトランザクション分離レベルが一貫しているわけではありません。読み取り:すべてが一貫した読み取りを使用している場合でも、スナップショットデータの定義は異なります。

スナップショットデータは、現在の行データの前の履歴バージョンです。複数の履歴バージョンがあり、行に複数のスナップショットデータがある場合があります。これは行マルチバージョンテクノロジーと呼ばれます。これによってもたらされる同時実行制御はマルチバージョンと呼ばれます。同時実行制御(マルチバージョン同時実行制御、MVCC)。

読み取りコミットおよび反復可能読み取り(InnoDBエンジンのデフォルトのトランザクション分離レベル)では、InnoDBは非ロックの一貫性のある読み取りを使用しますが、スナップショットの定義は異なります。読み取りコミット済みトランザクション分離レベルでは、非一貫性の読み取りは常に読み取りロックされます。最新のスナップショット行のデータ。RepeatableReadトランザクション分離レベルでは、一貫性のない読み取りは常にトランザクションの開始時に行データバージョンを読み取ります。

トランザクションが2つある場合、トランザクションは次のようになります。
ここに画像の説明を挿入
この時点でトランザクションAは開始され、ID 1の行データを読み取りましたが、トランザクションは終了していません。この時点で、トランザクションBに次の変更を加えます。
ここに画像の説明を挿入
同じトランザクションBはコミットされていませんこの時点で、XロックがID 1の行に追加されます。この時点でID 1の行がトランザクションAで読み取られた場合、InnoDBエンジンのトランザクション分離レベルが読み取りコミットされた場合または繰り返し読み取り、非ロックが使用されます一貫した読み取りはスナップショットの内容を読み取り、結果は次のようになります(この期間中にこれらの2つのトランザクションのみが存在すると仮定する
ここに画像の説明を挿入
と、現時点ではスナップショットデータのバージョンは1つのみになります) :トランザクションBが
ここに画像の説明を挿入
この時点でトランザクションコミットした場合トランザクションAがこの時点で再度読み取りを行った場合ID 1のデータの場合、トランザクション分離レベルが異なると結果が異なり
ます。1。読み取りコミットトランザクション分離レベルの場合:
ここに画像の説明を挿入
常に読み取ります行の最新バージョン。行がロックされている場合は、最新のスナップショットを読み取ります。したがって、ID 1の行データを読み取ることはできません
ここに画像の説明を挿入
。2。繰り返し可能読み取りトランザクション分離レベルの場合:
ここに画像の説明を挿入
常に行データを読み取ります。トランザクションの開始なので、Aはこの時点でID 1の行を読み取ることができるはず
ここに画像の説明を挿入
です:時間の観点から上記の例を示します:
ここに画像の説明を挿入
Read Committedトランザクション分離レベルの場合、ACIDの分離に違反します。

デフォルトでは、InnoDBエンジンのSELECT操作は一貫した非ロック読み取りを使用しますが、場合によっては読み取り操作をロックする必要があり
ます。1.SELECT…FORUPDATE:読み取り行レコードにXロックを追加します。他のトランザクションでは、これらの回線のロックはすべてブロックされます。
2.選択…共有モードでのロック:読み取り行レコードにSロックを追加します。他のトランザクションでは、ロックされたレコードにSロックを追加できますが、Xロックトランザクションはブロックされます。

上記の2つのステートメントがトランザクションで使用されると、トランザクションがコミットされ、ロックが解放されます。

一貫性のある非ロック読み取りの場合、読み取り行がSELECT…FOR UPDATEで使用されていても、読み取ることができます。

InnoDBエンジンには、カウンターの値を取得するための自動インクリメント値を含む各テーブルの自動インクリメントカウンターがあります。

SELECT MAX(auto_inc_col)
FROM tableName
FOR UPDATE;

挿入操作は、このカウンタ値に1を加算し、それを自己インクリメント列に割り当てます。実装方法はAUTO-INC Lockingです。この実装方法は、特別なテーブルロックメカニズムを使用します。挿入パフォーマンスを向上させるために、ロックはトランザクションが終了した後に解放されます。代わりに、自己増加値に挿入されたSQLが完了した直後に解放されます。この方法は、自己成長列を使用した同時挿入のパフォーマンスが低く、前の挿入の完了を待つ必要があります。INSERT…SELECTなどの大量のデータを含む挿入の場合、他の挿入操作はブロックされます。

MySQL 5.1.22以降、InnoDBエンジンは軽量のミューテックス自動インクリメント実装メカニズムを提供し、自動インクリメント値挿入のパフォーマンスを大幅に向上させます。このバージョン以降、InnoDBエンジンはパラメーターinnodb_autoinc_lock_modeを提供します。デフォルト値は1です。 。、以下は自己増加挿入分類
です。1。INSERTのようなものは、INSERT、REPLACE、INSERT…SELECT、REPLACE…SELECT、LOADDATAなどのすべての挿入ステートメントを指します。
2.単純挿入とは、INSERT、REPLACEなど、挿入前に挿入された行数を確認できるステートメントを指しますが、INSERT…ON DUPLICATE KEY UPDATE(MySQL固有のステートメント、挿入レコードの主キーが競合する場合)は含まれません。テーブルで、Updateを実行します)このタイプのSQLステートメント。
3.一括挿入とは、INSERT…SELECT、REPLACE…SELECT、LOAD DATAステートメントなど、挿入前に挿入された行数を判別できないステートメントを指します。
4.混合モードの挿入とは、挿入の値の一部が自己増加することを意味します。たとえば、INSERTステートメントが複数行のデータを同時に挿入する場合、自己インクリメント列の値の一部が指定され、パーツはNULL値またはINSERT…ONDUPLICATE KEYUPDATEです。

パラメータinnodb_auoinc_lock_modeのオプション値:
1。これ0は、バージョン5.1.22より前の自己インクリメントの実現です。つまり、テーブルロックのAUTO-INCLockingメソッドです。
2.これ1はパラメータのデフォルト値です。単純挿入の場合、この値はミューテックスを使用してカウンタをメモリに蓄積します。一括挿入の場合、従来のAUTO-INCロック方式が引き続き使用されます。このとき、ロールバック操作に関係なく、自己付加価値の成長は継続しています。この場合、AUTO-INC Lockingメソッドを使用して自己増加値を生成すると、Simple insertの操作は、INSERTステートメントが終了する前にAUTO-INCLockingの解放を待機します。
3.2すべてのINSERTのような自己増加値は、最高のパフォーマンス方法であるミューテックスを介して生成されますが、いくつかの問題が発生します。同時挿入が存在するため、各挿入の自己増加値はそうではない場合がありますマスタースレーブレプリケーションがステートメントベースレプリケーションを使用している場合(マスタースレーブライブラリは同じSQLステートメントを使用します)、マスタースレーブライブラリの自動インクリメント列の値は並行性のために異なる場合があります。この場合、Row-を使用する必要があります。ベースレプリケーション(マスターライブラリとスレーブライブラリは同じ変更を行い、行の変更はSQLステートメントではなくbinlogに記録されます)。

MyISAMはテーブルロックを使用するため、自己成長は同時挿入の問題を考慮する必要はありません。

InnoDBエンジンでは、自己増加列の値はインデックスである必要があり、インデックスの最初の列です。2番目の列の場合、エラーが報告されます。MyISAMにはこの問題はありません。

CREATE TABLE tab (
    a    INT   auto_increment,
    b    INT,
    KEY(b, a)
) ENGINE = InnoDB;

実行:
ここに画像の説明を挿入
外部キー列の場合、この列に明示的にインデックスを追加しないと、InnoDBエンジンが自動的にインデックスを追加するため、テーブルのロックを回避できます。

外部キー列を挿入または更新するには、最初に親テーブル、つまりSELECT親テーブルのレコードをクエリする必要があります。親テーブルのSELECTの場合、一貫性のある非ロック読み取りは使用されません。これにより、データの不整合。SELECT…LOCKIN SHARE MODEを使用し、親テーブルにSロックを追加します。この時点でXロックがすでに親テーブルに追加されて
ここに画像の説明を挿入
いる場合、子テーブルでの操作はブロックされます。上の図では、2つのトランザクションにCOMMITまたはROLLBACKがなく、トランザクションAがid = 3の親テーブルの行にXロックを追加し、トランザクションBが次のことを行う必要があるため、この時点でBはブロックされます。 id = 3の親テーブルの行にSロックを追加すると、トランザクションBがブロックされます。トランザクションBが親テーブルにアクセスするときに一貫した非ロック読み取りを使用する場合、InnoDBのデフォルトのトランザクション分離レベルRepeatable Readの場合、ID 3の行が親テーブルで読み取られ、挿入操作を実行できます。トランザクションAがコミットした後、親テーブルにID 3のレコードがなくなり、親テーブルと子テーブルの間に不整合が生じます。この時点でinnodb_locksテーブルをクエリすると、次のようになります。InnoDB
ここに画像の説明を挿入
ここに画像の説明を挿入
エンジンには、3つの行ロックアルゴリズム設計があります
。1。レコードロック:単一行レコードをロックします。
2.ギャップロック:ギャップロック、レコード自体を除く範囲をロックします。
3.Next-Key Lock:2とともに使用して、範囲をロックし、レコード自体をロックします。これは、ギャップロックとレコードロックを組み合わせたロックアルゴリズムです。このアルゴリズムでは、InnoDBエンジンは、行クエリにこのロックアルゴリズムを使用します。さまざまなSQLクエリステートメントに対して、共有のNext-KeyLockと排他的なNext-を設定できます。Keyロック。

レコードロックは常にインデックスレコードをロックします。InnoDBエンジンテーブルの作成時にインデックスが設定されていない場合、ロックにはInnoDBエンジンの暗黙的な主キーが使用されます。

Next-Key Lockを示すために、最初にテーブルを作成します。

CREATE TABLE nkl (
    a    INT,
    PRIMARY KEY(a)
) ENGINE = InnoDB;

次のデータをテーブルに挿入します。

BEGIN;

INSERT INTO nkl 
SELECT 1;

INSERT INTO nkl 
SELECT 2;

INSERT INTO nkl 
SELECT 3;

INSERT INTO nkl 
SELECT 4;

INSERT INTO nkl 
SELECT 7;

INSERT INTO nkl 
SELECT 8;

COMMIT;

次に、次の2つのトランザクションを実行します。
ここに画像の説明を挿入
上記の場合、Next-Key Lockアルゴリズムでは、(-∞, 6]間隔内のすべての値がロックされ、挿入できるため、5または6が挿入されているかどうかに関係なく、トランザクションBがロックされます。現時点では9。単一値のインデックスクエリの場合、ギャップロックは必要ありません。レコードロックを追加するだけです。InnoDBエンジンは、それ自体で最小のアルゴリズムモデルを選択します。

上記の例のデモンストレーションプロセスは、InnoDBのデフォルトのトランザクション分離レベルで実行されます。つまり、反復可能読み取りモードでは、Next-KeyLockアルゴリズムがデフォルトの行およびレコードロックアルゴリズムです。

ロックにより、次の3つの問題が発生する可能性があります
。1。更新の喪失:複数のユーザーが同時にレコードを変更すると、更新が失われる可能性があります。つまり、ユーザーの更新操作が別のユーザーの更新操作によって上書きされます。次のような状況です。
(1)トランザクション1データの行をクエリし、ローカルメモリに配置して、ユーザー1に表示します。
(2)トランザクション2は、同じ行のデータを照会し、それをローカルメモリに入れて、ユーザー2に表示します。
(3)ユーザー1は、このレコードの行を変更し、データベースを更新して送信します。
(4)ユーザー2は、このレコードの行を変更し、データベースを更新して送信します。このとき、ユーザー1の変更は失われます。

更新が失われないようにするには、トランザクションをシリアル操作にする必要があります。手順(1)でレコードに排他ロックを追加できます(下図のfor update句)。このとき、トランザクション2は待機する必要があります。手順(1)(3)手順(2)を実行して、完了後にデータをクエリできます
ここに画像の説明を挿入
。2。ダーティ読み取り:ダーティデータとダーティページが異なります。ダーティページとは、バッファプールで変更されたが、変更されていないページを指します。ディスク、つまりメモリ内のページとページにフラッシュされますディスクのページ内のデータに一貫性がなく(もちろん、データがディスクにフラッシュされる前にREDOログが書き込まれています)、ダーティデータはバッファプールで変更されましたが、まだコミットされていません。

有効なデータはダーティページにあるため、ダーティページの読み取りはデータの整合性に影響しません。したがって、非同期に同期されたデータもパフォーマンスを向上させることができます。ダーティデータの読み取りとは、あるトランザクションが別のトランザクションでコミットされていないデータを読み取ることを意味し、データベースの分離に違反します。

トランザクション分離レベルのReadUncommittedは、ダーティリードで発生します。

3.繰り返し不可の読み取り:同じトランザクションで同じデータが複数回読み取られた場合、2回の読み取りデータの間隔で別のトランザクションが変更されたため、2回読み取られたデータが異なります。

トランザクション分離レベルのReadCommittedは、繰り返し不可能な読み取りにつながります。一般に、繰り返し不可能な読み取りの問題は許容されます。SQLサーバーとOracleデータベースのデフォルトの分離レベルは読み取りコミットです。

InnoDBエンジンは、Next-Key Lockアルゴリズムを使用して、繰り返し不可能な読み取りの問題を回避します。公式のMySQLドキュメントでは、繰り返し不可能な読み取りをファントム問題として定義しています。このアルゴリズムでは、インデックスのスキャンにより、スキャンされたインデックスだけでなくロックされます。 、また、これらのインデックスがカバーする範囲をロックするため、この範囲への挿入は許可されません。これにより、他のトランザクションがこの範囲にデータを挿入することによって引き起こされる繰り返し不可能な読み取りの問題が回避されます。InnoDBは、デフォルトのトランザクション分離レベルを使用します。アルゴリズムは、繰り返し不可能な読み取りの問題を回避します。

異なるロック間の互換性の関係により、あるトランザクションのロックは、別のトランザクションのロックが占有しているリソースを解放するのを待つ必要がある場合があります。これにより、ブロックされます。

InnoDBエンジンでは、パラメーターinnodb_lock_wait_timeoutを使用して待機をブロックする最大時間を制御し(デフォルトは50秒、動的パラメーター、実行時に調整可能)、パラメーターinnodb_rollback_on_timeoutを使用して進行中のトランザクションをロールバックするかどうかを設定します待機タイムアウト時(デフォルトはオフ、ロールバックなし、静的パラメーター)。

デフォルトでは、InnoDBはブロッキングおよびタイムアウトトランザクションをロールバックしませんが、コミットするため、エラーが発生する可能性があります。
ここに画像の説明を挿入
次に例を示します。トランザクションAで上記のテーブルにNext-Key Lockを追加します。
ここに画像の説明を挿入
ここに画像の説明を挿入
トランザクションBで、操作を挿入します。 :
ここに画像の説明を挿入
表示されているトランザクションBは実行されていませんが、実行された部分はコミットされています。

従来のデッドロック状況:
**ここに画像の説明を挿入**
上の図に示すように、InnoDBはデッドロックを検出しました。ほとんどのデッドロックは、人間の介入なしで検出できます。上記の例では、デッドロックが発生した後にトランザクションBがロールバックされ、ロックが解放され、トランザクションAがリソースを取得します。InnoDBエンジンは、デッドロックを除いてほとんどのエラー例外をロールバックしないため、デッドロック例外が発生したときに、トランザクションを再度ロールバックする必要はありません。

Oracleデータベースでのデッドロックの一般的な原因は、外部キーにインデックスが追加されないことです。InnoDBエンジンは、子テーブルの外部キー列にインデックスを自動的に追加し、インデックスが削除されると例外がスローされます。 。

ロックのアップグレードとは、行ロックをページロックにアップグレードしたり、ページロックをテーブルロックにアップグレードしたりするなど、ロックの範囲を拡大することを指します。一部のデータベース設計では、ロックは不足しているリソースと見なされます。ロックのオーバーヘッドを回避したい場合は、ロックをアップグレードできます。SQLサーバーがこれを行います。行、キー、またはページレベルのロックをより粗いロックに自動的にアップグレードします。必要に応じて、システムリソースを保護し、システムがロックを維持するためにメモリを使いすぎないようにします。これにより、効率はある程度向上しますが、同時パフォーマンスは低下します。

SQL Server 2005以降、行ロックが新たにサポートされますが、その設計はInnoDBエンジンとは完全に異なります。ロックのエスカレーションは、次の状況でも発生する可能性があります
。1。オブジェクトの単一のSQLステートメントによって保持されるロックの数がしきい値を超える(デフォルトは5000)、それが別のオブジェクトである場合、ロックのエスカレーションは発生しません。
2.ロックリソースが占有しているメモリがアクティブメモリの40%を超えた場合。

InnoDBエンジンにはロックのアップグレードがありません。そのため、1つのロックは1,000,000のロックと同じであり、オーバーヘッドはありません。これはOracleデータベースに似ています。

おすすめ

転載: blog.csdn.net/tus00000/article/details/113727686