MySQL MVCCメカニズム原理の詳細な説明

MySQL MVCCメカニズム原理の詳細な説明

MVCCとは

MVCC は、Multi-Version Concurrency Control の略で、マルチバージョン同時実行制御のことです。 MVCC は同時実行制御方式であり、一般的にデータベース管理システムで使用され、プログラミング言語でデータベースやトランザクション メモリへの同時アクセスを実装します。

一般的に、MySQL データベースを使用する場合は、Innodb ストレージ エンジンを使用することが知られています。Innodb ストレージ エンジンはトランザクションをサポートしています。そのため、複数のスレッドが同時にトランザクションを実行すると、同時実行の問題が発生する可能性があります。このとき、同時実行性を制御できる方法が必要となり、MVCC がこの役割を果たします。

MySQL ロックとトランザクション分離レベル

MVCC メカニズムの原理を理解する前に、まず MySQL のロック メカニズムとトランザクション分離レベルを理解する必要があります。MyISAM ストレージ エンジンは別として、Innodb ストレージ エンジンには行ロックとテーブル ロックの 2 種類のロックがあります。テーブル ロックは 1 回の操作でテーブル全体をロックします。ロックの粒度は最も大きくなりますが、パフォーマンスは最も低く、デッドロックは発生しません。行ロックは一度に 1 行をロックします。ロックの粒度は小さく、同時実行性は高くなりますが、デッドロックが発生する可能性があります。

Innodb の行ロックは、共有ロック (読み取りロック) と排他ロック (書き込みロック) に分けられます。トランザクションが行に読み取りロックを追加すると、他のトランザクションは行の読み取りは許可されますが、書き込み操作は許可されません。他のトランザクションも行に書き込みロックを追加することはできませんが、読み取りロックは追加できます。

トランザクションが行に書き込みロックを追加すると、他のトランザクションはこの行に書き込むことはできませんが、読み取ることはできます。同時に、他のトランザクションはこの行に読み取り/書き込みロックを追加することはできません。

MySQL のトランザクション分離レベルを見てみましょう。これは次の 4 つのレベルに分かれています。

  1. コミットされていない読み取り: トランザクションは、他のトランザクションがまだコミットしていないデータを読み取ることができ、ダーティ リードが発生します。たとえば、給与テーブルがあるとします。最初にトランザクション A が開始され、次に ID 1 の従業員の給与が照会されます。この時点で給与が 1000 であるとします。このとき、トランザクション B も開始され、更新操作が実行され、ID 1 の従業員の給与が 100 減額されますが、トランザクションはコミットされません。このとき、トランザクション A のクエリ操作を再度実行すると、トランザクション B によって更新されたデータを読み取ることができます。この時点でトランザクション B がロールバックされると、トランザクション A は「ダーティ」なデータを読み取ります。トランザクション A が更新操作を実行すると、ファントム リードも発生する可能性があります。
  2. コミットされた読み取り: トランザクションは、コミットされた別のトランザクションによって変更されたデータのみを読み取ることができ、他のトランザクションがデータを一度変更してコミットした後、トランザクションは最新の値を照会できます。同じ例ですが、今回はトランザクション分離レベルが Read Committed であり、トランザクション B がトランザクションをコミットしないため、トランザクション A はトランザクション B によって更新されたデータを読み取ることができず、ダーティ データの生成を回避できます。しかし、トランザクション B がコミットされた後、トランザクション A は同じクエリを再度実行すると、データが変更されていることに気付きます。これは非反復読み取りと呼ばれ、同じトランザクションで同じクエリを複数回実行した結果が矛盾することを意味します。同時に、ファントム読み取りも依然として存在します。
  3. 繰り返し読み取り: トランザクションが初めてレコードを読み取った後、他のトランザクションがレコードの値を変更してコミットした場合でも、トランザクションがレコードを再度読み取るときは、毎回異なるデータを読み取るのではなく、最初に読み取った値を読み取ります。これが繰り返し読み取りです。この分離レベルは、非再現性の問題を解決しますが、ファントム読み取りが依然として発生する可能性があります。
  4. シリアル化: この分離レベルでは、同じレコードに対するすべての操作がシリアルであるため、ダーティ リードやファントム リードは発生しません。ただし、これは同時トランザクションではありません。

MySQL 元に戻すログ

MVCC は、最下層で Mysql の undo ログに依存しています。undo ログには、データベースの操作が記録されます。undo ログは論理ログであるため、レコードが削除されると、undo ログには対応する挿入レコードが記録されます。レコードが更新されると、undo ログには反対の更新レコードが記録されます。トランザクションが失敗してロールバックする必要がある場合は、undo ログの対応するコンテンツを読み取ることでロールバックできます。MVCC は undo ログを利用します。

MVCCの実装原則

MVCC の実装では、データベースの暗黙的なフィールド、UNDO ログ、および ReadView が使用されます。まず、暗黙のフィールドを見てみましょう。実際、MySQL はテーブルの各行の背後に次の隠しフィールドを暗黙的に記録します: DB_TRX_ID (最後に変更された (変更/挿入) トランザクションの ID)、DB_ROLL_PTR (このレコードの前のバージョンを指すロールバック ポインター)、および DB_ROW_ID (自動増分 ID。データ テーブルに主キーがない場合、クラスター化インデックスはデフォルトでこの ID を使用して作成されます)。

UNDO ログには 2 種類あります。挿入 UNDO ログは、新しいレコードが挿入されたときに生成される UNDO ログです。トランザクションがロールバックされたときにのみ必要で、トランザクションがコミットされた直後に破棄できます。更新 UNDO ログは、トランザクションが更新または削除されたときに生成される UNDO ログです。トランザクションがロールバックされたときだけでなく、スナップショットが読み取られたときにも必要です。したがって、気軽に削除することはできません。高速読み取りまたはトランザクション ロールバックがログに関係しない場合にのみ、対応するログがパージ スレッドによって均一にクリアされます。 MVCC は更新元に戻すログを使用します。

実際、UNDO ログにはバージョン チェーンが記録されます。データベースに次のようなレコードがあるとします。

ここで、このレコードを変更し、名前を tom に変更するトランザクション A があります。このときの操作フローは次のとおりです。

  • トランザクションAは最初に行レコードに行ロックを追加します。
  • 次に、行レコードを古いバージョンとしてUNDOログにコピーします。
  • コピー後、行名をtomに変更し、行のDB_TRX_IDの値をトランザクションAのIDに変更します。このとき、トランザクションAのIDは1であると仮定し、行のDB_POLL_PTRをUNDOログにコピーしたレコードに向けます。
  • トランザクションがコミットされると、ロックは解除されます

この時点での状況は以下のとおりです。

このとき、別のトランザクション B がこのレコードを変更し、年齢を 28 に変更します。このときの操作フローは次のとおりです。

  • トランザクションBは行レコードに行ロックを追加します。
  • 行レコードは、古いバージョンとして UNDO ログにコピーされます。UNDO ログにすでにレコードがある場合は、リンク リストのヘッダーとして、行レコードの UNDO ログの先頭に新しい UNDO ログが挿入されます。
  • コピー後、行のageを28に変更し、行のDB_TRX_IDの値をトランザクションBのIDに変更します。このとき、トランザクションBのIDは2であると仮定し、行のDB_POLL_PTRをUNDOログにコピーしたレコードに向けます。
  • トランザクションがコミットされた後にロックを解除する

この時点での状況は以下のとおりです。

上記から、異なるトランザクションまたは同じトランザクションによって同じレコード行に加えられた変更により、レコード行の UNDO ログによってバージョン チェーンが形成されることがわかります。UNDO ログ チェーンの先頭は最新の古いレコードであり、チェーンの末尾は最も古いレコードです。

ここで、ある状況を想定してみましょう。トランザクション A もトランザクション B もコミットされていないとします。この時点で、tom というレコードを変更し、age を 30 に変更するトランザクション C があります。その後、トランザクションがコミットされます。トランザクション C の ID は 3 です。同様に、レコードが UNDO ログに挿入されます。この時点で、UNDO ログ バージョン チェーンの最初のレコードの DB_TRX_ID は 3 です。

現在、tom という名前のレコードをクエリするトランザクション D があります。この時点で、スナップショット読み取りが有効になります。スナップショットは、トランザクションの開始時にクエリ操作によってトリガーされるデータ スナップショットです。ロック解除読み取りは、繰り返し読み取り分離レベルでのデフォルトのスナップショット読み取りです。スナップショット読み取りとは対照的に、現在の読み取りもあります。すべての更新操作は現在の読み取りです。スナップショット読み取り中に読み取りビューが生成されます。トランザクションがスナップショット読み取りを実行すると、データベースの現在のスナップショットが生成され、現在アクティブなトランザクションの ID が記録および維持されます。トランザクション ID は自動増分されるため、トランザクションが新しいほど ID が大きくなります。読み取りビューは可視性アルゴリズムに従っており、可視かどうかはある程度の判断が必要です。読み取りビューは、現在アクティブなトランザクション ID を記録するだけでなく、現在作成されている最大トランザクション ID も記録します。スナップショットを読み取るときは、読み取りビューと比較して可視性の結果を取得する必要があります。

読み取りビューでは、主に現在のトランザクションの ID とシステム内のアクティブなトランザクションの ID を比較します。比較ルールは次のとおりです。

まず、読み取りビューには、読み取りビューが生成された時点でシステム内でアクティブなトランザクションIDの配列が含まれており、一時的にid_listと呼ばれます。

次に、読み取りビューはid_list内の最小のトランザクションIDを記録します。これは一時的にlow_idと呼ばれます。

最後に、読み取りビューは、読み取りビューが生成された時点でシステムに割り当てられていないトランザクションIDも記録します。これは、現在の最大トランザクションID + 1で、一時的にhigh_idと呼ばれます。

  • 現在のトランザクションIDがlow_idより小さい場合、現在のトランザクションは表示されます。
  • 現在のトランザクション ID が high_id より大きい場合、現在のトランザクションは表示されません。
  • 現在のトランザクションはlow_idより大きくhigh_idより小さいです。次に、それがid_listにあるかどうかを判断します。ある場合、アクティブなトランザクションがまだコミットされていないことを意味します。現在のトランザクションは表示されませんが、アクティブなトランザクション自体には表示されます。id_listにない場合は、現在のトランザクションは表示されます。

可視性の結果が非表示の場合、比較のために、DB_ROLL_PTR を使用して、UNDO ログからレコードの DB_TRX_ID を取得する必要があります。特定の条件を満たす DB_TRX_ID が見つかるまでバージョン チェーンをトラバースすると、この DB_TRX_ID を持つ古いレコードが、現在のトランザクションが確認できる最新の古いバージョンになります。

上記はMySQL MVCCメカニズム原理の詳細な説明の詳細な内容です。MySQL MVCCメカニズム原理の詳細については、123WORDPRESS.COMの他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • MySQL トランザクション分離レベルと MVCC の詳細な説明
  • MySQL トランザクション分離はどのように実現されますか?
  • MySQL トランザクション分離レベルとロックメカニズムの問題に関する深い理解
  • MySql8.0 のトランザクション分離レベルエラーの問題を解決する
  • MySQL マルチバージョン同時実行制御 MVCC の基本原理の分析
  • MySQL マルチバージョン同時実行制御 MVCC の実装
  • Mysql MVCC マルチバージョン同時実行制御の詳細
  • MYSQL トランザクション分離レベルと MVCC

<<:  IIS web.config でクロスドメイン アクセスを設定する方法

>>:  HTMLドキュメントタイプの詳細な説明

ブログ    

推薦する

VMware で Centos7 ブリッジ ネットワークを構成する手順の詳細な説明

VMware仮想マシンでのCentos7ブリッジネットワーク構成の完全な手順は参考用です。具体的な内...

MySQL ルートパスワードを変更する複数の方法 (推奨)

方法1: SET PASSWORDコマンドを使用する MySQL -u ルート mysql> ...

JavaScript の instanceof メソッドの手動実装

1. instanceofの使用法instanceof演算子は、コンストラクター関数のprototy...

Dockerを使用してLaravel開発環境を構築するための完全な手順

序文この記事では、Docker を使用して、ローカル コンピューターにインストールされている開発スイ...

Linux で Hadoop クラスターをインストールするための詳細な手順

目次1. usrディレクトリにHadoopディレクトリを作成し、インストールパッケージをそのディレク...

MYSQL ローカルインストールと問題解決

序文この記事はかなり詳細で、少し面倒です。他のチュートリアル ドキュメントでは多くの手順が省略されて...

...

【Webデザイン】E-WebTemplates の美しい海外の Web ページ テンプレート (FLASH+PSD ソース ファイル+HTML) を共有します

これらはすべて海外のE-WebTemplates WebサイトからのWebページテンプレートであり、...

ウェブデザインのためのロイヤルブルーのカラーマッチング入門

古典的な色の組み合わせは力と権威を伝え、強いロイヤルブルーはあらゆる古典的な色の組み合わせの中心的な...

MySQL データ型における DECIMAL の使用法の詳細な説明

MySQL データ型における DECIMAL の使用法の詳細な説明MySQL のデータ型には、INT...

Linux での MySQL 8.0.11 のインストールに関するチュートリアル

1. 公式サイトにアクセスしてインストールパッケージをダウンロードしますダウンロードリンク: クリッ...

nginxリバースプロキシのyum設定の詳細な手順

パート0 背景社内のイントラネットサーバーは直接インターネットにアクセスすることはできませんが、外部...

Tomcat のプレースホルダーによるポート設定方法 (パラメータ指定方式)

仕事で必要になったため、インターネットで多くの情報を見つけましたが、それらはすべてコピーアンドペース...

MySQLソースコマンドの使い方の紹介

目次ネット上の質問から生まれた思考MySQL ソースコマンドネット上の質問から生まれた思考今日仕事中...

CSS3のwebkit-box-reflectを巧みに使用して、さまざまな動的効果を実現します。

かなり前の記事で、 -webkit-box-reflectプロパティについて説明しました。リフレクシ...