|
上記の 2 つの状況は、複数のトランザクションが 1 つのデータに対して同時に動作する場合に発生する可能性がある問題で、特定のトランザクションの動作が上書きされ、データが失われる可能性があります。
LBCC はデータ損失を解決します
LBCC、ロックベースの同時実行制御。
ロック メカニズムを使用すると、現在のトランザクションがデータを変更する必要がある場合、現在のトランザクションはロックされます。現在のデータを同時に変更できるのは 1 つのトランザクションだけであり、他のトランザクションは待機する必要があります。ロックを解除するための操作を行います。
MVCC はデータ損失を解決します
MVCC、マルチバージョン同時実行制御、マルチバージョン同時実行制御。
バージョンを使用して同時実行状況でのデータの問題を制御します。トランザクション B がアカウントの変更を開始し、トランザクションが送信されないとき、トランザクション A がアカウント残高を読み取る必要がある場合、トランザクション B は次の時点で読み取られます。操作の前に口座残高のコピー データを変更しますが、トランザクション A が口座残高データを変更する必要がある場合、トランザクション B がトランザクションをコミットするまで待つ必要があります。
MVCC を使用すると、データをロックせずにデータベースを読み取り、ロックせずに通常の SELECT リクエストを実行できるようになり、データベースの同時処理能力が向上します。 MVCC の助けを借りて、データベースは READ COMMITTED や REPEATABLE READ などの分離レベルを実装できます。ユーザーは現在のデータの前または以前の履歴バージョンを表示して、ACID の I 機能 (分離) を確保できます。
InnoDB の MVCC 実装ロジック
InnoDB ストレージ エンジンによって保存される MVCC データ
InnoDB の MVCC は、各行の非表示列が実装された後に 2 つのレコードを保存します。 行を保存するトランザクション ID (DB_TRX_ID)、および行 を保存するロールバック ポインター (DB_ROLL_PT)。新しいトランザクションが開始されるたびに、新しいトランザクション ID が自動的に増加します。トランザクションの開始時に、トランザクション ID は、現在のトランザクションの影響を受ける行トランザクション ID に配置されます。クエリを実行する場合、現在のトランザクション ID と各行に記録されているトランザクション ID を比較する必要があります。
REPEATABLE READ 分離レベルで MVCC がどのように動作するかを見てみましょう。
SELECT
InnoDB は、次の 2 つの条件に従ってレコードの各行をチェックします。
InnoDB はバージョンのみを検索します。現在のものよりも前のデータ行のトランザクション バージョン (つまり、行のトランザクション番号が現在のトランザクションのトランザクション番号以下であること)。これにより、トランザクションによって読み取られる行が以前にすでに存在していることが保証されます。トランザクションが開始されるか、トランザクション自体によって挿入または変更された。
削除された行は、トランザクション ID とトランザクションを読み取る前の状態のバージョンによって判断する必要があり、上記 2 つの条件を満たすレコードのみがクエリ結果として返されます。 。
INSERT
InnoDB は、新しく挿入された各行の行バージョン番号として現在のトランザクション番号を保存します。
DELETE
InnoDB は、削除された各行の行削除 ID として現在のトランザクション番号を保存します。
UPDATE
InnoDB は、新しいレコード行を挿入し、現在のトランザクション番号を行のバージョン番号として保存し、現在のトランザクション番号を元の行に行削除識別子。
ほとんどの読み取り操作をロックせずに実行できるように、これら 2 つの追加トランザクション番号を保存します。この設計により、データ読み取り操作が非常にシンプルになり、パフォーマンスが非常に向上し、また、規格を満たす行のみが確実に読み取られるようになります。欠点は、レコードの各行に追加の記憶域スペース、より多くの行チェック、および追加のメンテナンス作業が必要になることです。
MVCC は、REPEATABLE READ と READ COMMITIED の 2 つの分離レベルでのみ機能します。 READ UNCOMMITIED は、現在のトランザクション バージョンに準拠するデータ行ではなく、常に最新のデータ行を読み取るため、他の 2 つの分離レベルは MVCC と互換性がありません。 SERIALIZABLE は、読み取られたすべての行をロックします。
mysql での MVCC の実装は、アンドゥ ログと読み取りビューに依存しています。
アンドゥ ログ
さまざまな動作に応じて、アンドゥ ログは アンドゥ ログの挿入と アンドゥ ログの更新#の 2 つのタイプに分類されます。
# 挿入操作のみが記録されるため、挿入操作中に生成される元に戻すログ現在のトランザクション自体の場合、このレコードは他のトランザクションには表示されないため、トランザクションがコミットされた後、パージ操作を実行せずに挿入取り消しログを直接削除できます。
パージの主なタスクは、データベース内で del マークが付けられたデータを削除することです。さらに、元に戻すページもバッチでリサイクルします。
最初のデータベース挿入時のデータの状態:
-
更新取り消しログ:
更新または削除操作中に生成された取り消しログ。既存のレコードに影響を与えるため、MVCC メカニズムを提供するために、更新取り消しログはトランザクションの送信時に削除できません。代わりに、トランザクションの送信時に履歴リストに配置され、パージ スレッドが実行されるのを待ちます。最後の削除操作。
データが初めて変更されるとき:
別のトランザクションが現在のデータを 2 回目に変更するとき:
同時トランザクション操作中にそれぞれの UNDO ログを書き込むときに競合が発生しないようにするために、InnoDB はロールバック セグメントを使用して同時書き込みと UNDO ログの永続性を維持します。ロールバック セグメントは、実際には Undo ファイルを整理する方法です。 読むビュー
RU(READ UNCOMMITTED) 分離レベルの場合、すべてのトランザクションはデータベースの最新の値を直接読み取ることができ、SERIALIZABLE 分離レベルの場合、すべてのリクエストはロックされ、同期的に実行されます。したがって、これら 2 つのケースでは、Read View のバージョン管理を使用する必要はありません。
RC(READ COMMITTED) および RR(REPEATABLE READ) の場合、分離レベルは上記のバージョン管理によって実装されます。 2 つの分離セクターの下での中心的な処理ロジックは、すべてのバージョンのうちのどのバージョンが現在のトランザクションに表示されるかを決定することです。この問題を解決するために、InnoDB は ReadView デザインをデザインに追加しました。ReadView には主に現在のシステム内のアクティブな読み取りおよび書き込みトランザクションが含まれており、それらのトランザクション ID をリストに入れます。 . 、このリストに m_ids という名前を付けます。
クエリ時にバージョンチェーンデータが表示されるかどうかの判定ロジック:
アクセスされたバージョンの trx_id 属性値が m_ids 内の最小のトランザクション ID より小さい場合このバージョンのトランザクションは ReadView を生成する前にコミットされているため、現在のトランザクションからこのバージョンにアクセスできることを示します。
アクセスされたバージョンの trx_id 属性値が m_ids リスト内の最大のトランザクション ID より大きい場合、このバージョンを生成したトランザクションが ReadView の生成後に生成されたことを示します。したがって、このバージョンは現在のトランザクション アクセスでは使用できません。
アクセスされたバージョンの trx_id 属性値が m_ids リスト内の最大のトランザクション ID と最小のトランザクション ID の間にある場合は、trx_id 属性値がm_ids リスト。そうである場合は、ReadView の作成時にこのバージョンを生成したトランザクションがまだアクティブであり、このバージョンにアクセスできないことを意味します。そうでない場合は、ReadView の作成時にこのバージョンを生成したトランザクションがコミットされたことを意味します、このバージョンにアクセスできます。
例:
分離レベルでの READ COMMITTED ReadView
データを読み取るたびに、すべてがReadView (m_ids リスト)
Time |
トランザクション 777 | トランザクション 888
|
トランザクション 999 |
##T1 | begin; |
|
|
T2 |
##開始; |
begin; |
|
T3
UPDATE ユーザー SET 名 = 'CR7' WHERE ID = 1; |
|
|
| #T4
| ##...
|
|
T5 |
UPDATE ユーザー SET name = 'Messi' WHERE id = 1;
|
| SELECT * FROM user where id = 1;
|
T6 |
#コミット;
|
|
| ##T7
|
#UPDATE ユーザー セット名 = 'Neymar' WHERE id = 1; |
|
| ##T8
|
| #SELECT * FROM user where id = 1;
|
T9
|
| ##UPDATE ユーザー セット名 = 'Dybala' WHERE id = 1;
|
| T10 |
#コミット; |
|
##T11
|
|
| #SELECT * FROM user where id = 1;
|
これは、上記の状況における ReadView の分析です。
時点 T5 での SELECT ステートメント:
現時点でのバージョン チェーン:
This SELECT 文を実行すると現在のデータバージョンチェーンは上記の通り 現在のトランザクション 777 とトランザクション 888 が送信されていないため、この時点でアクティブなトランザクションの ReadView のリスト m_ids: [777, 888]、したがって、クエリ ステートメントは、現在のバージョン チェーン内の m_ids 未満の最大のバージョン データに基づきます。つまり、Mbappe がクエリされます。
時点 T8 の SELECT ステートメント:
現時点でのバージョン チェーンの状況:
この時点で SELECT ステートメントが実行されます。現在のトランザクション 777 は送信され、トランザクション 888 は送信されていないため、チェーンは上記のとおりです。したがって、この時点でアクティブなトランザクションの ReadView のリスト m_ids: [888] であるため、クエリ ステートメントは m_ids の最大バージョン データよりも小さいチェーンの現在のバージョンに基づきます。つまり、メッシがクエリされます。
時点 T11 の SELECT ステートメント:
現時点でのバージョン チェーン情報:
この時点で SELECT ステートメントが実行され、現在のデータ バージョン チェーンは上記の通りです。現在のトランザクション 777 とトランザクション 888 が送信されているため、この時点でアクティブなトランザクションの ReadView リストは空であるため、クエリ ステートメントは現在のデータベースの最新データを直接クエリします。つまり、ディバラは尋問される。
概要: READ COMMITTED 分離レベルを使用するトランザクションは、各クエリの開始時に独立した ReadView を生成します。
#REPEATABLE READ 分離レベルの ReadView #トランザクション開始後の最初のデータ読み取り時に ReadView (m_ids リスト) を生成します
#時間
トランザクション 777 |
トランザクション 888 |
#トランザクション 999
|
|
T1
begin;
|
|
|
T2 |
| 開始;
開始; |
| #T3 | UPDATE ユーザー セット名 = 'CR7' WHERE ID = 1;
|
|
| ##T4
|
... |
|
| T5
UPDATE ユーザー SET name = 'Messi' WHERE id = 1; |
##SELECT * FROM user where id = 1; |
|
T6
| コミット; |
|
|
##T7
|
| UPDATE ユーザー SET name = 'Neymar' WHERE id = 1;
|
| #T8 |
|
SELECT * FROM user where id = 1; |
| T9
|
| UPDATE user SET名前 = 'ディバラ' WHERE id = 1;
|
| T10 |
| コミット;
|
##T11 |
|
|
## SELECT * FROM user where id = 1;
|
#SELECT ステートメント (時点 T5): |
現在のバージョン チェーン:
|
A ReadView は、時点 T5 で生成されます。 select ステートメントが現在実行されています。この時点で、 | m_ids
の内容は [777,888] であるため、ReadView の表示可能なバージョンに基づいてクエリされるデータは Mbappe です。
時点 T8 の SELECT ステートメント:
現在のバージョン チェーン:
現在のトランザクション 999 トランザクション。 ReadView は時点 T5 で生成されているため、ReadView は現在のトランザクションで 1 回だけ生成されるため、T5 の m_id はこの時点でもまだ使用されています: [777,999]、つまりこの時点のクエリ データまだムバッペだ。
時点 T11 の SELECT ステートメント:
現在のバージョン チェーン:
この時点の状況は T8 とまったく同じです。 ReadView は時点 T5 で生成されているため、ReadView は現在のトランザクションで 1 回だけ生成されるため、T5 の m_id はこの時点でもまだ使用されています: [777,999]、つまりこの時点のクエリ データまだムバッペだ。
MVCC の概要:
いわゆる MVCC (Multi-Version Concurrency Control、マルチバージョン同時実行制御) とは、 の使用を指します。 READ COMMITTD 、 REPEATABLE READ
これら 2 つの分離レベルのトランザクションは、通常の SEELCT 操作の実行時に記録されたバージョン チェーンにアクセスするため、
読み取り-書き込み、書き込み-読み取り システムのパフォーマンスを向上させるために、操作は同時に実行されます。
推奨学習: mysql ビデオ チュートリアル