ホームページ  >  記事  >  データベース  >  MySQL トランザクションと MVCC によって達成される分離レベルについて話しましょう

MySQL トランザクションと MVCC によって達成される分離レベルについて話しましょう

WBOY
WBOY転載
2022-01-26 17:11:102773ブラウズ

この記事では、MySQL トランザクションの分離レベルと MVCC による分離レベルの実装方法に関連するいくつかの問題について説明します。

MySQL トランザクションと MVCC によって達成される分離レベルについて話しましょう

#データベース トランザクションの概要

トランザクションの 4 つの主要な特徴 (ACID)

  1. 原子性: トランザクションの最小作業単位。すべてが成功したか、すべてが失敗したか。

  2. 一貫性: トランザクションの開始と終了後、データベースの整合性は破壊されません。

  3. 分離: 異なるトランザクションは相互に影響しません。4 つの分離レベルは、RU (コミットされていない読み取り) と RC (コミットされた読み取り)、RR (反復可能読み取り) です。 )、SERIALIZABLE (シリアル化)。

  4. 耐久性 (耐久性): トランザクションが送信された後のデータへの変更は永続的であり、システムに障害が発生した場合でも失われることはありません。

トランザクションの分離レベル

UnCommitted/RU

を再び読み取ります。

dirtyと呼ばれます。 reading では、あるトランザクションはコミットされていないデータを別のトランザクションから読み取ることができます。コミットされていないトランザクションはロールバックされる可能性があるため、この分離レベルは最も安全性が低くなります。

Read Committed/RC

Non-repeatable read とも呼ばれる、トランザクションが別のトランザクションを読み取りました。送信された変更されたデータにより、一貫性のない結果が生じます。現在のトランザクションの異なる時点で同じデータを読み取ることによって取得されます。

たとえば、次の例では、SessionA がトランザクション中に異なるデータを 2 回クエリしていることがわかります。その理由は、現在の分離レベルが RC であり、セッション A のトランザクションはセッション B によって送信された最新データを読み取ることができるためです。

#発生時刻セッション Aセッション B12select * from user where id=1;(张三)34select * from user where id =1 ;(李思)ユーザー セット名 = ' Wang Er' を更新where id=1;(デフォルトの暗黙的トランザクションコミット)select * from user where id=1;(Wang Er)##Repeatable Read/RR
begin;

update user set name='李思' where id=1; (デフォルトの暗黙的コミットトランザクション)

5

6

ファントム読み取り とも呼ばれるトランザクション読み取りは、他のトランザクションによって送信されたデータですが、RR 分離レベルでは、このデータの現在の読み取りは 1 回しか読み取ることができません。現在のトランザクションでは、何度読み取られても、データは依然として、そのトランザクションに対して読み取られた値です。最初の読み取り後に他のトランザクションがこのデータを変更して送信するため、変更されません。したがって、読み出されるデータが必ずしも最新のデータであるとは限らず、ファントムリードともなります。

例: セッション A で初めてデータが読み取られるとき、送信されたデータを変更する後続のトランザクションは、セッション A によって読み取られるデータ値に影響を与えません。これは

繰り返し読める読書 です。

#発生時刻

セッション Aセッション Bbegin;select * from user where id=1;(张三)update user set name='李思' where id=1; (デフォルトの暗黙的トランザクション送信)select * from user where id =1 ;(Zhang San)##5ユーザー セット名 = ' Wang Er' を更新where id=1; (デフォルトの暗黙的なトランザクション送信)6
1
2
3
4


select * from user where id=1;(Zhang San)
#シリアル化可能
データベースの読み取りまたは書き込み操作はすべてシリアル行実行であり、現在の分離レベルは同時実行のみをサポートしています。すべての操作はキューの実行を必要とします。したがって、この分離レベルにあるすべてのデータは最も安定していますが、パフォーマンスも最悪になります。データベースのロック実装は、この分離レベルのより粒度が小さいバージョンです。

#発生時刻

セッション A

セッション B
1 begin;
2
#begin;
3
ユーザー セット名を更新='李四' where id=1;
4 select * from user where id=1;(待て、待て)
5
コミット;
6 選択* id=1 のユーザーから;(李思)

トランザクションと MVCC の原則

同じデータを同時に操作する異なるトランザクションによって引き起こされる問題

例:

##1begin;##23456現金で 100 元を引き出し、残高は 900 元に修正されます89 ##トランザクションのコミット (残高=1100) 900)
#発生時間 セッションA セッションB


begin;

残高確認 = 1,000 元
残高確認 = 1,000 元
##入金額は 100 元で、修正残高は 1100 元です

トランザクションをコミット (残高=1100)

#発生時刻##1開始;##開始;お問い合わせ残高 = 1,000 元お問い合わせ残高 = 1,000 元入金額は 100 元、修正残高は 1100 元です現金 100 元を引き出し、残高は 900 元に修正されます取引を送信 (残高 = 1100) ##9

上記の 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 つの条件に従ってレコードの各行をチェックします。

  1. InnoDB はバージョンのみを検索します。現在のものよりも前のデータ行のトランザクション バージョン (つまり、行のトランザクション番号が現在のトランザクションのトランザクション番号以下であること)。これにより、トランザクションによって読み取られる行が以前にすでに存在していることが保証されます。トランザクションが開始されるか、トランザクション自体によって挿入または変更された。

  2. 削除された行は、トランザクション ID とトランザクションを読み取る前の状態のバージョンによって判断する必要があり、上記 2 つの条件を満たすレコードのみがクエリ結果として返されます。 。

INSERT

InnoDB は、新しく挿入された各行の行バージョン番号として現在のトランザクション番号を保存します。

DELETE

InnoDB は、削除された各行の行削除 ID として現在のトランザクション番号を保存します。

UPDATE

InnoDB は、新しいレコード行を挿入し、現在のトランザクション番号を行のバージョン番号として保存し、現在のトランザクション番号を元の行に行削除識別子。

ほとんどの読み取り操作をロックせずに実行できるように、これら 2 つの追加トランザクション番号を保存します。この設計により、データ読み取り操作が非常にシンプルになり、パフォーマンスが非常に向上し、また、規格を満たす行のみが確実に読み取られるようになります。欠点は、レコードの各行に追加の記憶域スペース、より多くの行チェック、および追加のメンテナンス作業が必要になることです。

MVCC は、REPEATABLE READ と READ COMMITIED の 2 つの分離レベルでのみ機能します。 READ UNCOMMITIED は、現在のトランザクション バージョンに準拠するデータ行ではなく、常に最新のデータ行を読み取るため、他の 2 つの分離レベルは MVCC と互換性がありません。 SERIALIZABLE は、読み取られたすべての行をロックします。

mysql での MVCC の実装は、アンドゥ ログと読み取りビューに依存しています。

アンドゥ ログ

さまざまな動作に応じて、アンドゥ ログは アンドゥ ログの挿入アンドゥ ログの更新#の 2 つのタイプに分類されます。

  • ##insert undo log:

# 挿入操作のみが記録されるため、挿入操作中に生成される元に戻すログ現在のトランザクション自体の場合、このレコードは他のトランザクションには表示されないため、トランザクションがコミットされた後、パージ操作を実行せずに挿入取り消しログを直接削除できます。

パージの主なタスクは、データベース内で 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 リスト)

セッションA セッションB
2
#3
4
5
6
8

取引をキャンセル (残高は 1000 元に回復) )
##T1 begin;T2T3#T4##...UPDATE ユーザー SET name = 'Messi' WHERE id = 1;SELECT * FROM user where id = 1;#コミット;#UPDATE ユーザー セット名 = 'Neymar' WHERE id = 1;##T8 #SELECT * FROM user where id = 1;##UPDATE ユーザー セット名 = 'Dybala' WHERE id = 1;T10#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 リスト) を生成します

Time トランザクション 777 トランザクション 888 トランザクション 999



##開始;
begin;
UPDATE ユーザー SET 名 = 'CR7' WHERE ID = 1;



T5

T6

##T7



T9


#コミット;
##T11

#時間T1begin;開始;#T3UPDATE ユーザー セット名 = 'CR7' WHERE ID = 1;##T4...T5コミット;UPDATE ユーザー SET name = 'Neymar' WHERE id = 1;#T8T9UPDATE user SET名前 = 'ディバラ' WHERE id = 1;T10コミット;## SELECT * FROM user where id = 1;m_ids の内容は [777,888] であるため、ReadView の表示可能なバージョンに基づいてクエリされるデータは Mbappe です。
トランザクション 777 トランザクション 888 #トランザクション 999


T2

開始;




UPDATE ユーザー SET name = 'Messi' WHERE id = 1;
##SELECT * FROM user where id = 1; T6
##T7



SELECT * FROM user where id = 1;




##T11


#SELECT ステートメント (時点 T5):
現在のバージョン チェーン:
A ReadView は、時点 T5 で生成されます。 select ステートメントが現在実行されています。この時点で、

時点 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 COMMITTDREPEATABLE READ

これら 2 つの分離レベルのトランザクションは、通常の SEELCT 操作の実行時に記録されたバージョン チェーンにアクセスするため、
読み取り-書き込み

書き込み-読み取り システムのパフォーマンスを向上させるために、操作は同時に実行されます。

推奨学習: mysql ビデオ チュートリアル

以上がMySQL トランザクションと MVCC によって達成される分離レベルについて話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。