1. はじめにご存知のとおり、MySQL の RR 分離レベルでデータをクエリすると、データが他のトランザクションの影響を受けないことを保証できます。ただし、RC 分離レベルでは、他のトランザクションがコミットされている限り、コミット後のデータが読み取られます。では、トランザクション分離の原則は何でしょうか。これはどのように達成されるのでしょうか?これは MVCC メカニズム ( 注: MySQL の InnoDB エンジンが高性能な同時実行をサポートできる理由は、MySQL の MVCC メカニズム (undo ログ、Read-View などによる) によるものですが、この記事では MVCC については詳しく説明しません。 参考:「MySQL実践45講義」シリーズ。説明は比較的わかりやすいのですが、それでも理解が必要です。例えば、ビュー配列に関する部分は明確に説明されていないと思うので、資料と自分の見解を組み合わせて記録します! 2. RC および RR 分離レベルRCとRRの分離レベルをそれぞれ開きます。まず、口座テーブルがあると仮定します。トランザクションABCが開始される前、口座の残高は1です。つまり、 select balance from account =1; # 結果は1です 2.1. RRトランザクション分離レベルでのクエリ結果RRトランザクション分離レベルで3つのトランザクションが開かれると、次の操作が異なる期間に実行されます。
時間を論理的に3つの段階に分け、結果を分析します。
最後に、トランザクション A の読み取り残高の結果は 1 です。当然、RR は繰り返し読み取りの略で、つまり、実行中にトランザクションによって表示されるデータは、トランザクションの開始時に表示されたデータと常に一致しています。現在のトランザクションがコミットされているかどうかは、データに影響を与えません。スナップショットに基づいてデータを読み取るだけでよく、これがスナップショット読み取りです。しかし、私たちが議論したいのは、それを MVCC メカニズムの下でどのように実装するかということです。 注意: begin/start transaction コマンドはトランザクションの開始点ではありません。トランザクションは実際には InnoDB テーブルを操作する最初のステートメントが実行された後に開始されます。トランザクションをすぐに開始する場合は、一貫性のあるスナップショットでトランザクションを開始するコマンドを使用できます。 2.2. RCトランザクション分離レベルでのクエリ結果同様に、RC 分離下でトランザクション ABC を開始し、トランザクション A の最終残高結果を観察します。 最後に、トランザクション A によって読み取られた残高の結果は 2 です。当然、RC は read committable の略で、文字通り、他のトランザクションがコミットされている限り、現在のトランザクションの最新の現在の値をすぐに読み取ることができることを意味します。これが現在の読み取りです。しかし、私たちが議論したいのは、それを MVCC メカニズムの下でどのように実装するかということです。 実際、これは、MVCC の実装で使用される一貫性のある読み取りビューが、RC (Read Committed) および RR (Repeatable Read) 分離レベルの実装をサポートするために使用されるためです。 3. MVCCにおけるトランザクション分離の実装MVCC がトランザクションの分離を実現する仕組みについて説明する前に、MVCC がトランザクションの分離を実現する仕組みをより深く理解するために、ビュー配列や一貫性のあるビューなどの概念を理解する必要があります。 3.1. データ行の複数のバージョン ROWInnoDB の各トランザクションには、トランザクション ID と呼ばれる一意のトランザクション ID があります。これは、トランザクションの開始時に InnoDB トランザクション システムに適用され、適用順に厳密に増分されます。 データの各行にも複数のバージョンがあります。トランザクションがデータを更新するたびに、新しいデータ バージョンが生成され、このデータ バージョンのトランザクション ID にトランザクション ID が割り当てられ、行 trx_id として記録されます。同時に、古いデータ バージョンは保持する必要があり、新しいデータ バージョンには、それを直接取得するための情報 (undo_log ファイルを通じて見つかる) が含まれている必要があります。 つまり、データ テーブル内のレコードの行には実際には複数のバージョン (行) があり、各バージョンには独自の行 trx_id があります。 ある時点で 3 つの更新トランザクションが実行されるデータ行 ROW のマルチバージョン管理プロセスについての理解を深めるために、次の図を描いてください。 この図から次のことがわかります。
マルチバージョンの原則とデータ行 ROW の実装を理解すると、InnoDB がスナップショットを定義および作成する方法を理解するのに役立ちます。 3.2 ビュー配列以下の部分は、資料にある原文からの抜粋です。特に赤字部分は理解しにくいかもしれませんので、ご自身の理解と組み合わせて絵を描いてください。 これは、トランザクションの開始時に InnoDB がスナップショットを定義する方法です。どのトランザクション操作を無視でき、どの操作をスナップショットに保存する必要がありますか?これは次のように理解できます。トランザクションは、起動時に「開始時間に基づいて、開始前にデータ バージョンが生成された場合はそれを認識します。開始後に生成された場合はそれを認識せず、以前のバージョンを見つける必要があります」と宣言するだけで済みます。 実装の点では、InnoDB は各トランザクションの配列を構築し、トランザクションが開始された時点で現在「アクティブ」であるすべてのトランザクション ID を保存します。 「アクティブ」とは、開始されているがまだ送信されていないことを意味します。配列内のトランザクション ID の最小値が低水準点として記録され、現在のシステムで作成されたトランザクション ID の最大値に 1 を加えた値が高水準点として記録されます。このビュー配列と最高水準点は、現在のトランザクションの一貫したビュー (読み取りビュー) を構成します。 低水位と高水位についての私の理解: 最低水位標 = 現在開始されているがコミットされていないすべてのトランザクション セットの最小 ID 値 = 現在のトランザクションの前に開始されたがコミットされていない最後のトランザクションの最小 ID 値 (すべてのアクティブなトランザクションの最小 ID 値) ハイウォーターマーク = 現在のトランザクション ID (現在の ROW バージョン番号 / 行 trx_id) = 作成されたトランザクション ID の最大値 + 1 例えば、上記のRR分離レベルの下の3つのABCトランザクションを例として挙げます。
したがって、トランザクションAのビュー配列は[99]、トランザクションBのビュー配列は[99,100]、トランザクションCのビュー配列は[99,100,101]となります。つまり、ビュー配列の一般的な式は、[{現在のトランザクションが開かれた時点でのアクティブなトランザクション ID のコレクション}] です。 データ バージョンの可視性ルールは rowtrx_id と一貫性ビューの比較結果に基づいているため、一貫性ビューも理解する必要があります。 3.3. 一貫性ビュービュー配列を理解することで、一貫性のあるビューが容易になります。つまり、このビュー配列とハイ ウォーター マークが現在のトランザクションの一貫性のあるビュー (読み取りビュー) を構成します。 上記のRR分離レベルの下の3つのABCトランザクションを例として挙げます。
このように、トランザクション A の一貫性ビューは [99,100]、トランザクション B の一貫性ビューは [99,100,101]、トランザクション C の一貫性ビューは [99,100,101,102] になります。つまり、一貫性のあるビューの一般的な式は、[{現在のトランザクションが開かれた時点でのアクティブなトランザクション ID のセット}、現在の行 trx_id] です。 上記のフローチャートの結果を分析します。 最初の有効な更新バージョンは、残高 = 2 を更新するトランザクション C です。この時点で、最新バージョンの rowtrx_id = 102 ですが、トランザクション ABC の前のアクティブなトランザクションの最新バージョンの rowtrx_id は 99 であるため、この時点で 99 が履歴バージョン 1 になります。 2 番目の有効な更新バージョンは、残高 = 3 を更新するトランザクション B です。この時点で、最新バージョンの rowtrx_id = 101、rowtrx_id = 102 が履歴バージョン 1 になり、rowtrx_id = 99 が履歴バージョン 2 になります。 トランザクションAがクエリを実行すると、トランザクションBは送信されていませんが、生成された(id, balance) = (1, 3)が最新バージョンになります。トランザクションAがデータを読み取ると、一貫性のあるビューは[99, 100]になります。読み取られたデータは現在のバージョンから切り取られ、行trx_idと比較されるため、次の処理が行われます。
最後に、トランザクションAがいつクエリを実行しても、表示されるデータは、一貫性のあるビュー[99, 100]によって生成されたスナップショットデータ(1, 1)、つまりrowtrx_id=90のときのデータです。これを一貫性のある読み取りと呼びます。 要約: トランザクション ビューの場合、その更新が常に表示されることに加えて、次の 3 つの状況があります。
ここで、このルールを使用して、図のクエリ結果を判断します。トランザクション A が開始されると、トランザクション A のクエリ ステートメントのビュー配列が生成されます。この時点では、
3.4 現在の読み取りとスナップショットの読み取り3.4.1 現在の読み取りとスナップショットの読み取りルールもちろん、この一貫性のある読み取りのロジックによれば、トランザクション B はトランザクション C が実質的に balance=2 を更新した後に更新されますが、トランザクション B のビュー配列はトランザクション C で生成されるため、理論的には、トランザクション B はデータ (id、balance)=(1、1) (スナップショット/履歴バージョン) を参照するべきではないでしょうか?現在のバージョン(1、2)のデータは表示されません。残高を更新した直後にトランザクションBのデータが(1, 3)になるのはなぜですか? トランザクション B が更新前にデータを 1 回選択すると、表示される値は確かに balance=1 になりますが、履歴バージョンで更新を実行することはできません。そうしないと、トランザクション C の更新が失われます。したがって、更新操作では、まず現在のバージョンを読み取ってから更新します。 つまり、データの更新は、まず読み取り、次に更新というルールがあります。読み取りは最新の値を読み取ることであり、これを「現在の読み取り」と呼びます。読み取りを行わずにクエリのみを実行すると、現在のスナップショットが読み取られ、「スナップショット読み取り」と呼ばれます。したがって、トランザクション B は残高を更新する前に、まず最新バージョン (1, 2) をクエリし、次にそれを (1, 3) に更新します。トランザクション A によってクエリされたスナップショット データは (1, 1) であり、最新バージョン (1, 3) ではありません。 3.4.2 現在の読み取りとスナップショット読み取りの説明現在の読み取り: 共有モードでのロックの選択 (共有ロック)、更新の選択、更新、挿入、削除 (排他ロック) などの操作はすべて現在の読み取りです。つまり、レコードの最新バージョンを読み取ります。読み取り時には、他の同時トランザクションが現在のレコードを変更できないようにする必要があり、読み取られたレコードはロックされます。 スナップショット読み取り: ロック解除された選択操作はスナップショット読み取り、つまりロック解除された非ブロッキング読み取りです。スナップショット読み取りの前提は、分離レベルがシリアル レベルではないことです。シリアル レベルでのスナップショット読み取りは、現在の読み取りに退化します。これはマルチバージョン管理に基づいているため、スナップショットの読み取りでは必ずしも最新バージョンのデータが読み取られるわけではなく、以前の履歴バージョン (スナップショット データ) が読み取られる場合があります。 3.4.3 RC読み取りコミットのルールを表示するコミットされた読み取りのロジックは、繰り返し可能な読み取りのロジックに似ています。 それらの主な違いは次のとおりです。 繰り返し読み取り分離レベルでは、トランザクションの開始時に一貫性のあるビューを作成するだけで、トランザクション内の他のクエリはこの一貫性のあるビューを共有します。読み取りコミット分離レベルでは、各ステートメントが実行される前に新しいビューが再計算されます。この場合、一貫性のあるスナップショットによるトランザクションの開始は、通常の starttransaction/begin と同等です。したがって、RC 分離レベルでは、トランザクション A とトランザクション B によってクエリされるデータは次のようになります。 トランザクション C は、すぐに残高 = 2 を更新し、自動的にコミットして最新バージョン (1, 2) を生成します。このとき、ビュー データ (1, 2) が再計算されます。トランザクション B は、最新バージョンが (1, 2) であることを見つけ、最新バージョンとしてバージョン (1, 3) に更新します。このときトランザクション B が選択した残高は 1 ではなく 3 です (トランザクション B が残高 = 3 を更新した後、新しいビューがすぐに計算され、このビューに基づいて取得されたデータが選択される)。この時点ではトランザクション B はまだ送信されておらず、トランザクション A からは見えません。そのため、トランザクション A はトランザクション C によって送信された最新バージョンを読み取ります (1、2)。 上記は、MySQL がトランザクション分離を実装する方法の詳細についての簡単な分析です。MySQL トランザクション分離の詳細については、123WORDPRESS.COM の他の関連記事に注目してください。 以下もご興味があるかもしれません:
|
>>: Docker を使用した RabbitMQ 環境のデプロイの詳細な紹介
目次1. ダウンロード2. nginxとnginx-vts-exporterをインストールする3. ...
1. 上部と下部のリストタグ: <dl>..</dl>:上dt下層dd: カ...
MySQL 5.7.13 Mac用インストールチュートリアル、非常に詳細で、以下のように記録されてい...
この例では、jQuery を使用してマウス ドラッグ イメージ機能を実装します。まず、ラッパーを設定...
目次1. MySQLレプリケーション関連の概念2. シンプルな1マスター1スレーブアーキテクチャの実...
導入データベース理論についてさらに学んでいくうちに、さまざまな分離レベルによって起こり得る問題につい...
最近、Vue プロジェクトについて知り、ElementUI でデータを xlsx および Excel...
ダイナミックレム1. まず、現在の長さの単位を紹介しましょうpx em Mの幅 / 漢字の幅 1em...
MySQL を使用してデータベースをクエリし、左結合を実行すると、関連付けられたフィールドの一部に...
ビルドを無効にするパーティション式では、次の構成はサポートされません。ストアドプロシージャ、ストアド...
MySQL では、LOAD_FILE() 関数はファイルを読み取り、その内容を文字列として返します。...
この記事の例では、WeChatアプレットで複数行のテキストスクロールを実装するための具体的なコードを...
目次1. jsメモリ2. 譲渡3. 浅いコピー4. ディープコピー序文:以下の記事を読む前に、記憶に...
1. MySQLサービスをシャットダウンする# service mysqld stop 2. rpm...
目次1. binlogの3つのモード1.ステートメントレベルモード2. 行レベルモード3. 混合モー...