MySQLのMVCCマルチバージョン同時実行制御の実装

MySQLのMVCCマルチバージョン同時実行制御の実装

1 MVCCとは何か

MVCC の正式名称は、マルチバージョン同時実行制御です。データベースへの同時アクセスを提供するときにトランザクション内でメモリ読み取りを処理し、書き込み操作が読み取り操作をブロックするという同時実行の問題を回避します。

たとえば、プログラマー A がデータベース内の特定のコンテンツを読み取り、プログラマー B がこのコンテンツを変更しているとします (変更が約 10 秒続くトランザクション内で行われると仮定)。A は、この 10 秒以内に不整合なデータを見る可能性があります。B がコミットする前に、A は常に整合性のあるデータを読み取ることができるのでしょうか。

これに対処するにはいくつかの方法があります。最初の方法は、ロックベースの同時実行制御です。プログラマー B がデータを変更し始めると、データをロックします。この時点でプログラマー A がデータを読み取ると、読み取れず待機状態になっていることがわかります。B が操作を完了した後にのみ、データを読み取ることができます。これにより、A が矛盾したデータを読み取らないことが保証されますが、プログラムの実行効率に影響します。もう 1 つあります。MVCC です。各ユーザーがデータベースに接続すると、特定の瞬間のデータベースのスナップショットが表示されます。B のトランザクションがコミットされる前に、A は常に特定の瞬間のデータベースのスナップショットを読み取り、B のトランザクションでのデータ変更は読み取りません。B のトランザクションがコミットされた場合にのみ、A は B の変更を読み取ることができます。

MVCC をサポートするデータベースは、特定のデータを更新するときに、古いデータを新しいデータで上書きするのではなく、古いデータを古いものとしてマークし、別の場所に新しいデータ バージョンを追加します。したがって、同じデータの複数のバージョンが保存されますが、最新のものは 1 つだけです。

MVCC は、時間の一貫性を実現するソリューションを提供します。MVCC でトランザクションを読み取る場合、通常、タイムスタンプまたはトランザクション ID を使用して、アクセスするデータベースの状態とデータのバージョンを決定します。読み取りトランザクションと書き込みトランザクションは互いに分離されており、相互に影響を与えません。同じデータが読み取りトランザクションと書き込みトランザクションの両方からアクセスされると仮定します。実際には、書き込みトランザクションは新しいデータ バージョンを作成し、読み取りトランザクションは古いデータ バージョンにアクセスします。読み取りトランザクションは、書き込みトランザクションがコミットされるまで、新しいデータ バージョンにアクセスしません。

MVCC を実装する方法は 2 つあります。1 つ目は、データベースに複数のバージョンのデータ レコードを保存する方法です。これらの異なるバージョンのデータが不要になると、ガベージ コレクターがこれらのレコードを回収します。この方法は、PostgreSQL および Firebird/Interbase で採用されています。SQL Server も同様のメカニズムを使用しますが、違いは、古いバージョンのデータがデータベースに保存されるのではなく、メイン データベースとは異なる別のデータベース (tempdb) に保存される点です。 2 番目の実装方法では、データベースに最新バージョンのデータのみを保存しますが、元に戻す操作を行うと、古いバージョンのデータが動的に再構築されます。この方法は、Oracle および MySQL/InnoDB で使用されます。

2. InnoDBのMVCC実装メカニズム

MVCC は行レベル ロックのバリエーションと見なすことができ、多くの場合ロック操作を回避できるため、オーバーヘッドが低くなります。ほとんどの MVCC 実装では非ブロッキング読み取り操作が実装され、書き込み操作では必要な行のみがロックされます。 InnoDB の MVCC 実装は、特定の時点でのデータのスナップショットを保存することによって実現されます。トランザクションの実行にどれだけ時間がかかっても、その中に表示されるデータは一貫しています。つまり、トランザクションは実行中に互いに影響を及ぼしません。以下では、InnoDB での MVCC の実装について簡単に説明します。

InnoDB の MVCC は、各レコード行の後に 2 つの隠し列を保存することで実装されています。1 つの列には行の作成時刻が保存され、もう 1 つの列には行の有効期限 (削除時刻) が保存されます。もちろん、ここでの時刻はタイムスタンプではなく、システム バージョン番号です。新しいトランザクションが開始されるたびに、システム バージョン番号が増加します。 RR 分離レベルでは、MVCC は次のように動作します。

操作を選択します。

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

削除された行のバージョンは未定義であるか、現在のトランザクション バージョン番号よりも大きいです。トランザクションによって読み取られた行が、トランザクションの開始前に削除されていないことを確認できます。

挿入操作。新しく挿入された行の現在のバージョン番号を行バージョン番号として保存します。

削除操作。削除された行の現在のバージョン番号を削除マークとして保存します。

更新操作。これは、挿入操作と削除操作の組み合わせになります。挿入された行は現在のバージョン番号を行バージョン番号として保存し、削除では現在のバージョン番号が削除マーカーとして元の行に保存されます。

古いデータは実際には削除されていないため、クリーンアップする必要があります。InnoDB は、クリーンアップを実行するためにバックグラウンド スレッドを開始します。具体的なルールは、現在のシステム バージョンよりもバージョン番号が小さい行を削除することです。このプロセスはパージと呼ばれます。

3. 簡単な例

テーブルyang(を作成 
    id int 主キー auto_increment, 
    名前varchar(20));
}

システムのバージョン番号は 1 から始まると仮定します。

入れる

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

トランザクションを開始します。
yang 値に挿入します (NULL、'yang')。
ヤン値(NULL,'long')に挿入します。
yang 値に挿入します (NULL、'fei')。
専念;

データ内の対応するテーブルは次のとおりです (最後の 2 つの列は非表示の列であり、クエリ ステートメントでは表示されません)

選択

InnoDB は各行を次の 2 つの条件と照合します。
a. InnoDB は、現在のトランザクション バージョンよりも前のバージョンのデータ行のみを検索します (つまり、行のシステム バージョン番号がトランザクションのシステム バージョン番号以下です)。これにより、トランザクションによって読み取られる行は、トランザクションの開始前に存在しているか、トランザクション自体によって挿入または変更されていることが保証されます。
b. 削除された行のバージョンは未定義であるか、現在のトランザクション バージョン番号より大きいため、トランザクションによって読み取られた行はトランザクションの開始前に削除されていないことが保証されます。
a と b の両方を満たすレコードのみがクエリ結果として返されます。

消去

InnoDB は、削除された各行の削除識別子として現在のシステム バージョン番号 (トランザクション ID) を保存します。
次の具体的な分析例を参照してください。
2 番目のトランザクション、ID は 2 です。

トランザクションを開始します。
ヤンから*を選択; //(1)
ヤンから*を選択します。 //(2)
専念;

仮定1

トランザクションID 2の実行中に(1)に達したときに、別のトランザクションID 3がテーブルにレコードを挿入すると仮定します。
3 番目のトランザクション ID は 3 です。

トランザクションを開始します。
yang 値に挿入します (NULL、'tian')。
専念;

表のデータは次のとおりです。

次に、トランザクション2の(2)を実行します。id=4のデータの作成時刻(トランザクションIDは3)、現在のトランザクションIDは2であり、InnoDBは現在のトランザクションID以下のトランザクションIDのデータ行のみを検索するため、トランザクション2の(2)ではid=4のデータ行は取得されません。トランザクション2の2つの選択文で取得されるデータは以下のもののみとなります。

仮定2

トランザクションID 2の実行中に、(1)を実行したと仮定します。トランザクションがトランザクション3を実行した後、トランザクション4を実行すると仮定します。
4番目のトランザクション:

トランザクションを開始します。  
id=1 の yang から削除します。
専念;

この時点でデータベース内のテーブルは次のようになります。

次に、トランザクションID 2のトランザクション(2)を実行します。SELECT検索条件に従って、作成時刻(作成トランザクションのID)が現在のトランザクションIDより小さく、削除時刻(削除トランザクションのID)が現在のトランザクションIDより大きい行を検索します。id=4の行は前述のとおりで、id=1の行は削除時刻(削除トランザクションのID)が現在のトランザクションIDより大きいです。そのため、トランザクション2(2)のselect * from yangもid=1のデータを検索します。そのため、トランザクション2の2つのselect文で検索されるデータは以下のようになります。

アップデート
InnoDB が UPDATE を実行すると、実際には新しいレコード行が挿入され、その作成時刻が現在のトランザクションの ID として保存されます。また、現在のトランザクション ID から UPDATE 対象の行への削除時刻も保存されます。

仮定3
トランザクション 2 (1) を実行した後、他のユーザーがトランザクション 3 と 4 を実行するとします。このとき、別のユーザーがこのテーブルに対して UPDATE 操作を実行します。
5番目のタスク:

トランザクションを開始します。
yang を更新し、name='Long' を設定します (id=2)。
専念;

更新の原則に従って、新しい行が生成され、変更される元の列の削除時間列にトランザクション ID が追加され、次のテーブルが作成されます。

トランザクション2の(2)の実行を続けます。SELECT文の検索条件に従って、次のテーブルが得られます。

トランザクション2(1)選択と同じ結果が得られます。

MySQL MVCC マルチバージョン同時実行制御の実装に関するこの記事はこれで終わりです。MySQL MVCC マルチバージョン同時実行制御に関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • MySQL マルチバージョン同時実行制御 MVCC の実装
  • MySQL マルチバージョン同時実行制御メカニズム (MVCC) ソースコードの詳細な説明
  • MySQL マルチバージョン同時実行制御 MVCC の詳細な研究
  • MySQL マルチバージョン同時実行制御 MVCC の基本原理の分析
  • MySQL マルチバージョン同時実行制御 MVCC の実装
  • Mysql MVCC マルチバージョン同時実行制御の詳細

<<:  ユーザーエクスペリエンスの概要

>>:  JavaScript 関数のカリー化

推薦する

Dockerイメージ送信コマンドcommitの動作原理と使い方の詳細な説明

ローカルでコンテナを作成した後、このコンテナに基づいてローカル イメージを作成し、このイメージを D...

Navicat の MySQL へのリモート接続の実装手順の分析

序文皆さんはリモート サーバーで開発を行っており、MySQL の使用率はかなり高いはずです。コマンド...

Vue カスタム箇条書きボックス効果 (確認ボックス、プロンプトボックス)

この記事の例では、参考のためにVueカスタムポップアップ効果の具体的なコードを共有しています。具体的...

.Net Core を使用して数千万のデータを MySQL にインポートする手順

目次事前準備実施方法: 1. 単一のデータを挿入する2. マージデータ挿入3. MySqlBulkL...

XMLとCSSスタイルの組み合わせ

学生.xml <?xml バージョン="1.0" エンコーディング=&qu...

Windows 10 に付属する仮想マシンのネットワークを設定するための詳細な手順 (グラフィック チュートリアル)

1. サーバー ホストをクリックし、右側の操作リストで [仮想スイッチ管理] をクリックして、仮想...

ReactアプリケーションにおけるDOM DIFFアルゴリズムの詳細な説明

目次序文VirtualDOM とは何ですか? VirtualDOMを使用する理由DOMレンダリングペ...

MySQLでANDとORを組み合わせる問題を解決する

以下のように表示されます。 SELECT prod_name,prod_price FROM pro...

MySQL ディープ ページング (数千万のデータを素早くページ分割する方法)

目次序文場合最適化まとめ序文バックエンド開発では、一度に大量のデータがロードされ、メモリやディスク ...

WeChat アプレットの日付と時刻のコンポーネント (年、月、日、時間、分)

この記事の例では、WeChatアプレットの日付と時刻コンポーネントの具体的なコードを参考までに共有し...

TypeScript のユニオン型、交差型、型ガード

目次1. ユニオンタイプ2. クロスオーバータイプ3. 型保護3.1 カスタム型保護3.2 保護の種...

Vueはチャットインターフェースを実装する

この記事の例では、チャットインターフェースの表示を実現するためのVueの具体的なコードを参考までに共...

Nginx サーバーが Systemd カスタム サービス プロセス分析を追加

1. nginxを例に挙げるyumコマンドを使用してNginxをインストールしましたSystemd ...

mysql5.7.19 winx64 解凍版のインストールと設定のチュートリアル

mysql 5.7.19 winx64解凍版のインストールチュートリアルを収録しました。具体的な内容...

MySQL テーブルがロックされているかどうかを照会する方法

具体的な方法: (推奨チュートリアル:MySQLデータベース学習チュートリアル)テーブルロックの状態...