MySQL における大規模オブジェクトのマルチバージョン同時実行制御の詳細な説明

MySQL における大規模オブジェクトのマルチバージョン同時実行制御の詳細な説明

MySQL 8.0: InnoDB のラージ オブジェクトに対する MVCC

この記事では、MySQL InnoDB ストレージ エンジンのラージ オブジェクト (LOB) 向けのマルチバージョン同時実行制御 (MVCC) 設計について説明します。 MySQL 8.0 には、JSON ドキュメントなどの大きなオブジェクトを部分的に更新できる新しい機能があります。 この部分更新機能により、LOB が部分的に更新されたときに LOB に対して MVCC が動作する方法が変更されました。 通常の更新(完全更新)の場合、MVCC は以前のバージョンと同じように動作します。 部分更新が行われない場合の MVCC の動作を確認し、次に LOB の部分更新の使用例を検討します。

MVCC 全般の最新情報

部分的な更新ではない更新を指すために、定期的な更新という用語を使用します。 MVCC を使用して大きなオブジェクトを定期的に更新する方法を例を挙げて説明します。 この目的のために、次のmtr(1)テストケースを使用します。

 テーブル t1 を作成します ( f1 int 主キー、 f2 longblob ) engine = innodb ; 
 t1に値(1、繰り返し('a'、65536))を挿入します。 
 
 トランザクションを開始します。 
 t1を更新し、f2 = repeat ( 'b' , 65536 ) を設定します。ここで、f1 = 1 です。 
 
 -- echo # 接続 con1: 
 -- MySQL クライアントを使用するユーザーの場合は、別のターミナル ウィンドウを開いて新しいリンクを確立する必要がある場合があります (以下も同様)。
 接続 ( con1 、 localhost 、 root 、 、 ) ; 
 -- echo # 古い値 'aaaaaaaaa' を確認する必要があります 
 t1からf1、right (f2、10)を選択し、f1で並べ替えます。 
 
 -- echo # 接続デフォルト: 
 接続のデフォルト; 
 con1を切断します。 
 専念 ; 
 
 テーブル t1 を削除します。

以下の説明を理解するには、上記のテストケースを注意深く理解することが非常に重要です。

テストシナリオは次のとおりです。

最初、テーブル t1 には 1 つのレコード (R1) が含まれています。
トランザクション trx1 はレコードを新しい値に更新します。
trx1 がまだアクティブな間に、別のトランザクション trx2 がレコードを読み取っています。 古い値が読み取られます。

テーブル t1 にはレコードが 1 つだけ (R1) 含まれています。 しかし、trx1 と trx2 には 2 つの異なる値が表示されます。 テーブルには実際には最新の値 (trx1 が参照する値) のみが含まれており、trx2 が参照する値またはレコードは UNDO ログ レコードから取得されます。 よりよく理解するために、下の図を見てみましょう。

初期状態: 更新操作前

次の図は更新操作前の状況を示しています。 元に戻すログは空です。 テーブルのクラスター化インデックスには 1 つの行が含まれます。 テーブルに LOB があります。 クラスター化インデックス レコードには LOB への参照が含まれています。

最終状態: 更新操作後

次に、更新操作後に何が起こるかを見てみましょう。

ここにいくつかの重要な観察事項を示します。

ユーザー表領域には、古い LOB と新しい LOB の 2 つの LOB があります。 古い LOB には、UNDO ログを通じてのみアクセスできます。 クラスター化インデックス レコードは新しい LOB を指します。
更新操作により、更新ベクトルを含む UNDO ログ レコードが作成されました。 この UNDO ログ レコードは古い LOB を指します。
クラスター化インデックス レコードは、DB_ROLL_PTR システム列を通じて UNDO ログ レコードを指します。 このロール ポインターは、クラスター化インデックス レコードの以前のバージョンを構築するために使用できる UNDO ログ レコードを指します。
UNDO レコードには LOB 自体は含まれません。 代わりに、ユーザー表領域に格納されている LOB への参照のみが含まれます。
UNDO ログ レコードに格納される LOB 参照は、クラスター化インデックス レコードに格納される LOB 参照とは異なります。

接続 1 のトランザクションによって実行される手順は次のとおりです。

トランザクションは R1 を調べ、クラスター化インデックス レコードを変更したトランザクションがコミットされていないことを判断します。 これは、そのレコードを読み取ることができないことを意味します (デフォルトの分離レベルは REPEATABLE READ であるため)。
R1 の DB_ROLL_PTR を調べて、UNDO ログ レコードを見つけます。 元に戻すログを使用して、R1 の以前のバージョンをビルドします。
このビルドでは古い R1 を読み取ります。 このバージョンはクラスター化インデックス レコードでは使用できないことに注意してください。 ただし、これは元に戻すレコードを使用してオンザフライで構築されます。
R1 は新しい LOB を指しますが、構築された R1 の古いバージョンは古い LOB を指します。 したがって、結果には古い LOB が含まれます。

部分的な更新が行われない場合、LOB の MVCC はこのように動作します。

MVCC 部分更新

部分更新の場合に MVCC がどのように機能するかを理解するために、別の例を見てみましょう。 JSON ドキュメントの部分的な更新は現在、関数 json_set() と json_replace() を通じてのみサポートされているため、別の例が必要になります。

 テーブル t2 を作成します ( f1 int 主キー、 j json ) engine = InnoDB ; 
 @elem_a を concat ( '"' 、 repeat ( 'a' 、 200 ) 、 '"' ) に設定します。 
 @ elem_a_with_coma を concat ( @ elem_a , ',' ) に設定します。 
 @json_doc を concat ( "[" 、 repeat ( @elem_a_with_coma 、 300 ) 、 @elem_a 、 "]" ) に設定します。 
 
 t2(f1,j)に値(1,@json_doc)を挿入します。 
 
 トランザクションを開始します。 
 t2を更新します。set j = json_set ( j , '$[200]' , repeat ( 'b' , 200 ) )、ここでf1 = 1です。 
 
 -- echo # 接続 con1: 
 接続 ( con1 、 localhost 、 root 、 、 ) ; 
 -- echo # 古い値 'aaaaaaaaa...' を確認する必要があります 
 t2からjson_extract(j,'$[200]')を選択します。 
 
 -- echo # 接続デフォルト: 
 接続のデフォルト; 
 con1を切断します。 
 専念 ;

シナリオは前の例と同じです。 longblob フィールドのみが JSON ドキュメントに変更されました。 ロードされるデータも JSON 形式に準拠するために若干異なります。

ヒント:上記の mtr テスト ケース (両方) にステートメント set debug = '+d, innodb_lob_print' を追加すると、LOB インデックスがサーバー ログ ファイルに出力されます。 LOB インデックスは挿入後すぐに印刷されます。 LOB インデックスにより、保存されている LOB オブジェクトの構造が提供されます。
部分更新操作の前

完全更新操作または部分更新操作前の初期条件は同じであり、上記に示されています。 ただし、下の図には追加情報がいくつか示されています。

グラフに表示されている他の情報を見てみましょう。

クラスター化インデックス レコードに格納されている LOB 参照に、LOB バージョン番号 v1 が含まれるようになりました。 最初の挿入操作中は 1 に設定され、部分更新ごとに増加します。
各 LOB データ ページには、LOB インデックス内にエントリがあります。 各エントリには LOB バージョン情報が含まれています。 LOB データ ページが変更されるたびに、新しいデータを含む新しい LOB データ ページにコピーされ、LOB バージョン番号が増加した新しい LOB インデックス エントリが作成されます。

追加情報は LOB バージョン番号です。 これは、クラスター化インデックス レコード内の LOB 参照と、LOB インデックスの各エントリで使用できます。

部分更新操作後

次の図は、部分更新操作後の状況を示しています。

ここで最も重要な最適化は、ユーザー表領域に LOB が 1 つだけ残っていることです。 変更する必要がある LOB データ ページのみを更新します。 部分更新操作後のこの単一の LOB には、LOB の古いバージョンと新しいバージョンの両方が含まれます。 これは、図の LOB データ ページの v1 および v2 ラベルによって示されています。

もう 1 つの重要な点は、UNDO ログとクラスター化インデックス レコード内の LOB 参照が同じ LOB を指していることです。 しかし、LOB 参照には異なるバージョン番号が含まれています。 UNDO ログ レコード内の LOB 参照には v1 (古いバージョン番号) が含まれ、クラスター化インデックス レコード内の LOB 参照には新しいバージョン番号 v2 が含まれます。

LOB バージョン番号の目的

上記のように、異なるバージョン番号を持つ異なる LOB 参照は同じ LOB を指します。 1 つの LOB には、異なるバージョンのパーツが含まれます。 LOB バージョン番号は、さまざまな LOB 参照が指す正しいバージョンを取得するために使用されます。 このセクションでは、これがどのように行われるかを見ていきます。

LOB インデックスには、LOB を構成する LOB ページのリストが含まれています。 これには、LOB データ ページのページ番号、各 LOB データ ページに含まれるデータの量、およびバージョン番号が含まれます。 このリストの各ノードは LOB インデックス エントリと呼ばれます。 各 LOB インデックス エントリには、古いバージョンのリストが含まれています。 上記の部分更新テストケースの構造を示す図を見てみましょう。

最初は、部分更新が実行される前は、LOB インデックスには合計 4 つのエントリが含まれています。 4 つのエントリのページ番号は 5、6、7、8 です。LOB インデックス エントリには古いバージョンはありません。 4 つのエントリのバージョン番号はすべて 1 です。

いくつかの更新が完了した後、ページ 9 がページ 7 に置き換わり、ページ 7 がページ 9 の古いバージョンとみなされていることに気付きました。ページ番号 9 にはバージョン番号 2 があり、ページ番号 7 にはバージョン番号 1 があります。

部分的な更新が完了した後、バージョン番号 1 の LOB 参照を通じて LOB にアクセスすると、ページ 5 の最初のインデックス エントリが調べられます。 バージョン番号は 1 です。インデックス エントリのバージョン番号が LOB 参照のバージョン番号以下の場合、そのエントリが読み取られます。 したがって、5ページ目が読み上げられます。 すると、6 ページのインデックス エントリが表示されます。 バージョン番号は 1 なので、それが読み取られます。 すると、ページ番号 9 のインデックス エントリが表示されます。 バージョン番号は 2 です。ただし、LOB 参照のバージョン番号は 1 です。インデックス エントリのバージョン番号が LOB 参照のバージョン番号より大きい場合、エントリは読み取られません。 ページ番号 9 のエントリはバージョン 2 なので、古いバージョンが表示されます。 ページ番号 7 の索引エントリがチェックされます。 バージョン番号は 1 なので、それが読み取られます。 この後、ページ番号 8 の索引エントリがチェックされます。 バージョン番号は 1 なので、これも読み込まれます。 これは、レガシー LOB にアクセスする方法です。

部分的な更新が完了した後、バージョン番号 2 の LOB 参照を通じて LOB にアクセスすると、ページ 5 の最初のインデックス エントリが調べられます。 バージョン番号は 1 です。インデックス エントリのバージョン番号が LOB 参照のバージョン番号以下の場合、そのエントリが読み取られます。 したがって、ページ番号 5、6、9、8 が順番に読み取られます。 バージョン番号は常に <= 2 なので、ページ番号 7 にアクセスするために古いバージョンを使用する必要はありません。

覚えておくべきことの 1 つは、LOB は InnoDB 内で独立して存在しないということです。 これはクラスター化インデックス レコードの拡張と見なされます。 LOB がトランザクションに対して可視かどうかは、LOB モジュールによって処理されません。 LOB モジュールはクラスター化インデックス レコードのみを処理します。 トランザクションが LOB にアクセスする場合、それは、クラスター化インデックス レコード内の DB_TRX_ID を使用して、LOB (LOB の特定のバージョンではない) を参照できることを決定したことを意味します。 したがって、LOB モジュールではその側面については心配する必要はありません。 私たちは、特定の LOB バージョン番号に対して正しいコンテンツを提供することにのみ重点を置いています。

結論は

この記事では、InnoDB で大きなオブジェクトに対して MVCC がどのように実行されるかを見てきました。 LOB が部分的に更新されると、複数の LOB 参照が同じ LOB を指す可能性があります。 ただし、バージョン番号は異なります。 これらの LOB バージョン番号を使用すると、正しい LOB コンテンツにアクセスできます。

この情報がお役に立てば幸いです。

MySQLをご利用いただきありがとうございます。

注:

(1)MtrはMini-transactionの略で、文字通り小さなものを意味します。論理的なものと比較して、物理的なものと呼びます。 Innodb ストレージ エンジンの基盤となるモジュールに属します。主に情報のロックとログ記録に使用されます。

要約する

上記はこの記事の全内容です。この記事の内容が皆さんの勉強や仕事に一定の参考学習価値を持つことを願っています。ご質問があれば、メッセージを残してコミュニケーションしてください。123WORDPRESS.COM を応援していただきありがとうございます。

以下もご興味があるかもしれません:
  • MySQL マルチバージョン同時実行制御メカニズム (MVCC) ソースコードの詳細な説明
  • MySQLのMVCCマルチバージョン同時実行制御の実装
  • MySQL 同時実行制御の原則に関する知識ポイント
  • MySQL マルチバージョン同時実行制御 MVCC の実装
  • MySQL の 4 つのトランザクション分離レベルの詳細な説明
  • MySQL の 4 つのトランザクション分離レベルの詳細な説明と比較
  • MySQL トランザクション分離とパフォーマンスへの影響の詳細な分析
  • Innodb トランザクション分離レベルと MySQL のロックの関係に関するチュートリアル
  • MySQL データベースのトランザクション分離レベル (トランザクション分離レベル) の概要
  • MySQL シリーズ 10 同時実行制御を実装するための MySQL トランザクション分離

<<:  Dockerfile echoは、指定されたファイル内の複数行のテキストを実装する方法を指定します。

>>:  Vue で axios を使用して画像をアップロードするときに発生する問題

推薦する

Vueはルールを使用してフォームフィールドの検証を実装します

Vue でフォーム フィールドを記述および検証する方法は多数あります。このブログでは、より一般的に使...

MySQL テーブルにはどのくらいの量のデータを保存できますか?

プログラマーは MySQL を扱う機会が多く、毎日触れているとも言えますが、MySQL テーブルには...

Linux で FastDFS を使用してイメージ サーバーを構築する

目次サーバー計画1. システムコンポーネントをインストールする2. fastdfsをインストールする...

ブートストラップテーブルの使い方のまとめ

この記事では、bootstrapテーブルの使い方を参考までに紹介します。具体的な内容は次のとおりです...

MySQL マスタースレーブレプリケーションの役割と動作原理の詳細な説明

1. マスタースレーブレプリケーションとは何ですか?マスタースレーブレプリケーションは、スレーブデー...

CentOS7にNginxをインストールして自動起動を設定する方法

1.公式サイトからインストールパッケージをダウンロードするhttp://nginx.org/en/d...

元のPATHを上書きしてコマンドが見つからないというメッセージが表示されるコマンド失敗の問題を解決する方法

同僚から、LINUX サーバー上の多くのコマンドが (コマンドが見つかりません) というプロンプトで...

MYSQL トランザクション チュートリアル Yii2.0 マーチャント引き出し機能

序文私はプログラマーとしてスタートした PHP プログラマーです。これまで、トレーニング コースで勉...

docker-maven-pluginを使用してデプロイメントを自動化する方法を説明します

1. docker-maven-pluginの紹介私たちの継続的インテグレーションプロセスでは、プロ...

Linux ネットワークプログラミング機能の簡単な分析

目次1.ソケットを作成する2. ソケットをバインドする3. 聞き手を作る。聞く4. 接続が受け入れら...

HTML フローティング フレーム (iframe 読み込み HTML) の設定と使用の例

コードをコピーコードは次のとおりです。 <!DOCTYPE html PUBLIC "...

ハイパーリンクに関するいくつかの質問

<br />ポテトチップスパーティーのこのエピソードに参加して、何人かの友達に会えてとて...

CSS3 で transform を使用した場合のフォントぼかしの解決方法の詳細な説明

この質問は非常に奇妙なので、あまり多くを語らずにコードに直接進みます。 .g-ダイアログラッパー{ ...

Vue ルーターにパラメータを渡すときにページを更新するとパラメータが失われる問題に対処する方法

目次概要方法1: params経由でパラメータを渡す方法2: クエリを通じてパラメータを渡す方法3:...