MySQL の分離レベル、ロック、MVCC の紹介

MySQL の分離レベル、ロック、MVCC の紹介

この記事の目的は、これらの概念とその機能の関係を明らかにすることです。 Mysql がトランザクションを開くときの、各 SQL ステートメントのロック操作と MVCC バージョン制御を理解します。議論を簡潔にするために、この記事では GAP ロック (ギャップ ロック、範囲ロック) は無視します。

多くの場合、高い同時実行性と高い可用性が求められます。質と量の観点から評価することを意味します。何でもこの2つの観点から分析することができます。 MySQL データベースでは、品質を確保するためにトランザクションが使用され、量を確保するために MVCC が使用されます。

取引

トランザクションを使用して、各 SQL ステートメントの結果が期待どおりに実行されるようにします。トランザクションには ACID プロパティが必須であると言われています。 ACID の 3 つ、つまり原子性、一貫性、永続性は、実際には同じことを表しており、SQL 実行結果の信頼性を保証します。分離はより複雑です。同時実行シナリオにおけるデータベースのパフォーマンスを表します。ただし、同時実行の量は固定されておらず、ビジネスによって要件が異なる場合があります。データベースをさまざまな同時実行シナリオに適応させるために、優れた人々が 4 つの分離レベルを定義しました。Read Uncommitted、Read Committed (RC)、Repeatable Read (RR)、および Serializable です。データベースの分離レベルが高くなると、データの同時実行能力は低下します。

分離レベル

標準分離レベルでのデータベースの動作については、https://www.jb51.net/article/116477.htm を参照してください。ここでは、共有ロックと排他ロック、読み取りと共有ロック、書き込みと排他ロックの概念についてのみ説明します。

RC 分離レベルでは、データが変更されると排他ロックが追加されます。トランザクションが終了するとロックが解除され、他のトランザクションはロックを読み取ることができなくなるため、ダーティ リードの問題が解決されます。 (共有ロックはその場で解除されます)
RR 分離レベルでは、読み取りデータに共有ロックが追加され、トランザクションの終了時に解放されます。他のトランザクションはデータを変更できないため、繰り返し不可能な読み取りの問題が解決されます。 (共有ロックトランザクションが終了し、解放されます)

実際、すべての操作はシリアル化されています。そして、Mysql はそれを最適化しました。1 つのトランザクションが読み取り中は、他のトランザクションは書き込みできません。また、1 つのトランザクションが書き込み中は、他のトランザクションは読み取りできません。これを実行しなくても、ダーティ リードと非反復読み取りの問題を解決することができます。 MVCCが登場しました。 (これにより、問題はますます複雑になり、RR 分離レベルで違いが現れ始めます。MySQL のデフォルトの分離レベルは RR です。)

MVCC

MVCC はマルチバージョン同時実行制御の略で、デュアルバージョン番号を使用してデータ分離の問題を解決します。 (「作成」にはバージョン番号があり、「削除」にもバージョン番号があり、変更操作は「削除」と「作成」に分かれています) 各トランザクションは、各テーブルの追加、削除、変更、およびクエリを開始するときにバージョン番号を生成します。各トランザクションは、このバージョン番号より小さい「作成」番号と、このバージョン番号より大きい「削除」番号を持つデータのみをクエリできます。この方法では、追加、削除、チェックの操作を同時に実行でき、変更操作のみをキューに入れる必要があります。この方法では、共有ロックがなくても、他のトランザクションがデータを変更した後、データのバージョン番号が自分のものより大きくなり、読み取らなくなるため、繰り返し不可能な読み取りの問題が解決されます。

RR 分離レベルでの MVCC 同時実行

MVCC の導入後、すべてが順調に進んでいるようです。しかし、2 つのトランザクションがデータを次々に更新し、そのデータを再度読み取るときに、何を読み取るかについて考えたことがありますか?ハハ、これは起こり得ないことです。変更操作はシリアルであり、別のトランザクションがこのトランザクションをコミットしてからでないと変更できません。では、質問を変えましょう。2 つのトランザクションが、データに対して +1 操作を連続して実行します。他のトランザクションがコミットされた後、このトランザクションは再度 +1 を実行し、データを再度読み取ります。このトランザクションは +1 または +2 のどちらの結果を読み取りますか? +2 が読み取られると、分離が破壊され、他のトランザクションによって送信されたデータが読み取られるのではないでしょうか。

しかし、これは確かにその通りです。他のトランザクションも送信されており、このトランザクションもデータを変更しています。もちろん、その後に+2を読む必要があります。元々0だったのに、今回の取引で1しか増えなかったのに、読んでみたら2になってしまい、ちょっと違和感があります。実際、標準の RR 分離レベルでは、すべての操作がシリアルであるため、このトランザクションがデータ行を読み取った後、他のトランザクションはこのデータを変更できません。このデータは常にこのトランザクションによってのみ操作されるため、分離は厳密に満たされます。ただし、MySQL の RR は読み取りと書き込みの同時実行性を強化します。2 つのトランザクションが同時にデータの一部を変更する場合にのみ、それらをシリアル化する必要があります。その他のすべての操作は並列で実行できます。つまり、この結果が引き起こされるのは、繰り返し不可能な読み取りが発生したためと思われます。しかし、この繰り返し不可能な読み取りは、実は私たちの直感と一致しています。このトランザクションでデータが変更された後は、当然ながら最新のデータを読み取る必要があります。

プロセスを分析するには:

データ作成バージョン番号は0です

トランザクション 1 のバージョン番号は 1、読み取りデータ値 = 0

トランザクション2のバージョン番号は2、変更されたデータ値+1=1、元のデータの削除バージョン番号は2、新しいデータの作成バージョン番号は2に更新され、コミットされます。

トランザクション 1 はデータ値 + 1 = 2 を変更します。(変更は現在の読み取りであるため、バージョン番号が最も大きいデータが常に読み取られ、読み取られる値は 1 になります。) 変更後、削除バージョン番号は 1 になります。

新しいデータ作成バージョン番号は1です

このトランザクションはデータ値=2を読み取ります

詳細な分析:

実は、上記の説明にも抜け穴があります。3 番目のトランザクション バージョン番号 3 があったらどうなるでしょうか?バージョン番号が 3 なので、トランザクション 1 と 2 のコミットされていないデータを直接読み取ることができますか?実際、MVCC では、各トランザクションには最小の表示バージョン low_limit_id もあります (トランザクション番号 >= low_limit_id のレコードは現在のトランザクションには表示されません)。これにより、現在実行中だがコミットされていないトランザクションが除外されます。たとえば、トランザクション 3 の場合、バージョン番号は 3 ですが、low_limit_id = 1 であるため、トランザクション 1 と 2 によって行われた変更は 3 には表示されません。

要約する

分離問題を解決するために、データを完全にコピーするなどの愚かな方法は使用されませんでした。従来のデータベースでは、共有ロックと排他ロックを使用して読み取り操作と書き込み操作をシリアル化していました。一方、MySQL では MVCC と排他ロックを使用して、読み取り操作と書き込み操作を並列で実行できます。 Mysql は、RR 未満の分離レベルでは従来の方法と同じように動作します。分離レベル RR では従来の方法とは異なります。これは、トランザクションがデータの一部を更新した後、他のトランザクションによってそのデータの一部にコミットされた変更を読み取ることができるという事実に反映されています。

以下もご興味があるかもしれません:
  • MySQL ストレージ エンジン MyISAM と InnoDB の違いの概要
  • MySQLでデータテーブルを作成するときにエンジンMyISAM/InnoDBを設定する
  • MySQL における楽観的ロック、悲観的ロック、MVCC の包括的な分析
  • MySQL InnoDB ストレージエンジンのメモリ管理の詳細な説明
  • MySQLのInnoDBストレージエンジンにおけるさまざまなロックの詳細な説明
  • MySQLのInnoDBストレージエンジンのデータページ構造の詳細な説明
  • MySQL ストレージ エンジン InnoDB と MyISAM
  • MYSQL データベース Innodb エンジン mvcc ロック実装原理

<<:  Docker に Kong ゲートウェイをインストールする方法の例

>>:  NGINXがウェブサイトのPV、UV、独立IPをカウントする方法の詳細な説明

推薦する

Nginx インストール エラーの解決方法

1. nginx-1.8.1.tar.gzを解凍する2. fastdfs-nginx-module-...

カラフルな時計効果を実現する JavaScript キャンバス

キャンバスを使ってカラフルな時計を書いてみよう! 1. タイトル(1)時計のケースが与えられ、ページ...

MySQLチュートリアルではストアドプロシージャを徹底的に理解します

目次1. ストアドプロシージャに関連する概念2. ストアドプロシージャの使用1) ストアドプロシージ...

HTML タグでの this の使用法の紹介

例えば:コードをコピーコードは次のとおりです。 <html> <ヘッド> &...

MySQL InnoDB のトランザクション特性を確保するにはどうすればよいですか?

序文「データベース トランザクションの特徴は何ですか?」と尋ねられたら、 ACID 特性である原子性...

Docker を使用して ELK ログ システムを構築する例

以下のインストールではすべて、インストール ルート ディレクトリとして ~/ ディレクトリが使用され...

Cronジョブを使用してCpanelでPHPを定期的に実行する方法

cpanel 管理バックエンドを開き、「詳細」オプションの下に「Clock Guardian Job...

メッセージボードにメッセージを追加および削除するための JavaScript

この記事では、JavaScript メッセージ ボードでメッセージを追加および削除する小さな例を詳細...

React における同期および非同期 setState の問題のコード分析

React は Facebook の社内プロジェクトとして始まりました。 React の出現は革命的...

ReactのEffectListの簡単な分析

目次EffectList コレクション最初のレンダリング時のEffectList EffectLis...

React refの使用例

目次refとは何かrefの使い方DOM要素に配置するクラスコンポーネントに配置する関数コンポーネント...

Linuxで権限が拒否された場合の解決策の詳細な説明

許可が拒否されました:その理由は、ファイルの読み取り、書き込み、作成、削除などの権限がないためです。...

SQL実装 LeetCode (176. 2番目に高い給与)

[LeetCode] 176. 2番目に高い給与従業員テーブルから 2 番目に高い給与を取得する ...

マージンのマージの問題を解決する

1. 兄弟要素の余白を結合する効果は次のようになります: (2 つの間の間隔は 150 ピクセルでは...

UbuntuにCMakeをインストールするいくつかの方法の詳細な説明

CMakeをインストール sudo apt をインストール cmake この方法はインストールが簡単...