MySQLの関連ロックについての簡単な理解

MySQLの関連ロックについての簡単な理解

この記事は主にInnoDBのロックに関する知識を素早く理解してもらうことを目的としています。

RocketMQの基本概念分析と詳細なソースコード分析

http://xiazai.jb51.net/202105/yuanma/RocketMQ_jb51.rar

なぜロックする必要があるのか

まず、なぜロックするのでしょうか?これ以上言う必要はないと思います。次のシーンを想像するだけでわかると思います。

ショッピングモールのトイレに行くとき、あなたはどうしますか?ドアをロックしてください。ドアをロックせずにトイレ使用中に突然ドアが開いたら、少し不適切と思われるかもしれません。

データについても同じことが言えます。同時実行シナリオでは、データがロックされていない場合、データの一貫性が直接破壊され、金銭が絡むビジネスの場合は、結果はさらに深刻になります。

ドアをロックする表現パック

ロックの分類

InnoDB のロックとは何ですか?実際、すでに多くのことを知っているはずです。たとえば、面接では、ストレージ エンジン MyISAM と InnoDB の違いについて質問されます。MyIASM にはテーブル ロックしかありませんが、InnoDB は行ロックとテーブル ロックの両方をサポートしていると答えるでしょう。楽観的ロックと悲観的ロックの違いは何かと尋ねられることもあります。

ロックには多くの概念や名詞があります。ロックについての完全な世界観を持っていなければ、理解するのは難しいでしょう。次に、これらのロックを分類します。

ロックの粒度に応じて

ロックの粒度に応じて、次のように分類できます。

  • テーブルロック
  • 行ロック

ページ ロックは BDB (BerkeleyDB) ストレージ エンジンにのみ存在する概念であるため、ここではページ ロックについては説明しません。ここでは主に InnoDB ストレージ エンジンについて説明します。

ロックのアイデアによると

ロックの考え方に応じて、次のように分けることができます。

  • 悲観的ロック
  • 楽観的ロック

ここでの悲観主義と楽観主義は、あなたが普段理解している名詞と同じ意味を持ちます。楽観的ロックでは、競合が発生する可能性が低いと想定し、必要な場合にのみロックを実行します。悲観的ロックでは、競合が発生する可能性が高いと判断されるため、必要かどうかに関係なくロック操作が実行されます。

相性に応じて

互換性に応じて、ロックは次のように分類できます。

  • 共有ロック
  • 排他ロック

共有ロックを持つリソースは他のユーザーと共有できますが、排他ロックが追加されると、他のユーザーはロックを取得せずに操作を実行できなくなります。

ロックの実装に応じて

ここでの実装は、InnoDB の特定の種類のロックです。

  • 意図ロック
  • レコードロック
  • ギャップロック
  • ネクストキーロック
  • 意図ロックを挿入する
  • AUTO-INCロック

このように分類されているとはいえ、錠前の名前がたくさんあると、少し混乱してしまうかもしれません。たとえば、 SELECT ... FOR UPDATE実行するとどのようなロックが追加されますか?

現象を通して本質を見なければなりません。本質とは何でしょうか?本質は、ロックがどのオブジェクトに追加されるかであり、これには簡単に答えることができます。

  • テーブルに追加されました
  • ラインに追加されました

行に追加されたロックの性質は何ですか?本質は、インデックスにロックを追加することです。

意図ロック

InnoDB は、行ロックやテーブルロックなど、さまざまな粒度のロックをサポートしています。たとえば、 lock tablesコマンドは、対応するテーブルに対して排他ロックを保持します。さまざまな粒度のロックをより実用的にするために、InnoDB はインテンション ロックを設計しました。

インテンション ロックは、次のトランザクションで使用されるロックの種類を示すテーブル レベルのロックです。次の 2 つの種類があります。

  • 共有意図ロック(IS)は、トランザクションがテーブル内のレコードに共有ロックを追加することを意図していることを示します。
  • 排他的意図ロック(IX)は排他的ロックである

たとえば、 select ... for share共有インテンション ロックを追加し、 SELECT .. FOR UPDATE排他インテンション ロックを追加します。ルールは次のとおりです。

  • トランザクションがテーブル内の行に対して共有ロックを取得したい場合、まずそのテーブルに対して共有意図ロックまたは排他意図ロックを取得する必要があります。
  • 同様に、排他ロックを取得したい場合は、まず排他意図ロックを取得する必要があります。

次の図は、これらのロックの組み合わせの排他性と互換性を示しています。

上記の表によれば、互いに互換性がある場合、対応するトランザクションはロックを取得できますが、互換性がない場合、互換性のないロックが解除されるまでロックを取得できません。

インテンション ロックはLOCK TBALES以外をブロックしないため、これを見ると疑問が生じるかもしれません。それで、それが何の役に立つのでしょうか?

引き続き例を使用して、トランザクション A が学生テーブルの id = 100 の行に対して共有ロックを取得し、次にトランザクション B が学生テーブルに対して排他ロックを適用する必要があるとします。これら 2 つのロックは明らかに競合しており、同じ行に対するものです。

InnoDB は、A がこのロックを取得したことをどのようにして知る必要があるのでしょうか? B+ ツリー全体をトラバースしますか?いいえ、答えは意図ロックです。トランザクション B が書き込みテーブルに排他ロックを適用すると、InnoDB はトランザクション A がすでにテーブルに対して意図的な共有ロックを取得していることを検出します。これは、共有ロックによってすでにロックされているレコードが学生テーブルに存在することを示します。現時点ではブロックされます。

さらに、インテンション ロックはLOCK TABLESなどの操作を除いて、他の操作をブロックしません。つまり、インテンション ロックは行レベルのロックではなく、テーブル レベルのロックとのみ競合します。インテンション ロックの主な目的は、誰かが行をロックしようとしているか、現在ロックしていることを示すことです。

図書館に行って本を探すときと同じように、本棚を一つ一つ探す必要はありません。サービスデスクに行って、コンピューターで検索するだけで、図書館にその本があるかどうかがわかります。

レコードロック

これはレコード ロックであり、行ロックの一種です。レコード ロックのロック オブジェクトは、そのデータ行に対応するインデックスです。インデックスについてよくわからない場合は、この記事をお読みください。

SELECT * FROM student WHERE id = 1 FOR UPDATEステートメントを実行すると、値 1 のレコード ロックがインデックスに追加されます。テーブルにインデックスがない場合はどうなりますか?この問題については、上記の記事でも説明されています。テーブルに主キーが定義されていない場合、InnoDB は非表示の RowID を作成し、この RowID を使用してクラスター化インデックスを作成します。後続のレコード ロックもこの非表示のクラスター化インデックスに配置されます。

id = 1 の行を更新するトランザクションを開始するときに、そのトランザクションをすぐにコミットせずに、id = 1 の行を更新する別のトランザクションを開始すると、 show engine innodb statusを使用したときに、 lock_mode X locks rec but not gap waitingメッセージが表示されます。

X は排他ロックを表します。このことから、レコード ロックは実際には共有ロック モードと排他ロック モードに分けられることがわかります。 FOR UPDATEを使用する場合は排他的になり、 LOCK IN SHARE MODEを使用する場合は共有されます。

上記のテキストに表示されるgapは、行ロックの別の実装であるギャップ ロックです。

ギャップロック

ギャップ ロックの場合、ロックされたオブジェクトもインデックスになります。ギャップロックをよりよく理解するために、例を見てみましょう。

更新するには、年齢が 18 から 25 の間の学生名を選択します。

ageの非クラスター化インデックスを作成したと仮定すると、このステートメントを実行すると、テーブルに実際に age 18-25 のデータがあるかどうかに関係なく、他のトランザクションが age 18-25 のデータをstudentテーブルに追加できなくなります。これは、ギャップ ロックの本質はインデックスの範囲をロックすることであり、InnoDB の基礎となる B+ ツリー内のインデックスのストレージは順序付けられているためです。

もう一つの例を挙げます。

SELECT * FROM student WHERE age = 10 FOR UPDATE;

ここでの age は一意のインデックスではなく、単純な非クラスター化インデックスであることに注意してください。このとき、 age = 10のデータにレコードロックが追加され、 age < 10ギャップがロックされます。現在のトランザクションがコミットされていない場合、 age < 10のデータを挿入しようとする他のトランザクションはブロックされます。

ギャップ ロックは、パフォーマンスと同時実行性を考慮した MySQL の妥協策であり、 Repeatable Read (RR) でのみ使用できます。現在のトランザクションの分離レベルが Read Committed (RC) の場合、MySQL はギャップ ロックを無効にします。

先ほど言ったように、レコード ロックは共有ロックと排他ロックに分かれており、ギャップ ロックも実際には同じです。しかし、レコード ロックとは異なり、共有ギャップ ロックと排他ギャップ ロックは相互に排他的ではありません。何が起こっているのでしょうか?

現象を通して本質を見極める必要があります。ギャップロックの目的は何でしょうか?

他のトランザクションがギャップにデータを挿入するのを防ぐため

共有ギャップ ロックと排他ギャップ ロックはこの目標において一貫しているため、同時に存在できます。

プロキーロック

ネクストキー ロックは、InnoDB における行ロックの実装の最後のタイプです。ネクストキー ロックは、実際にはレコード ロックとギャップ ロックの組み合わせです。つまり、隣接キー ロックは対応するインデックスにレコード ロックを追加し、さらに間隔をロックします。

ただし、すべての一時キー ロックがこのように動作するわけではありません。次の SQL の場合:

SELECT * FROM 学生 WHERE id = 23;

この場合、 id主キーであり、一意のインデックスです。他のトランザクションによってどれだけ多くのデータが挿入されても、 id = 23のデータは常に 1 つだけ存在します。現時点では、ギャップ ロックを追加する必要はまったくなく、同時実行性が低下します。したがって、使用されるインデックスがユニーク インデックスの場合、即時キー ロックはレコード ロックにダウングレードされます。

10、20、30 という 3 つのインデックス データ項目があるとします。一時的なキーロックの場合、ロック可能な範囲は次のようになります。

  • (∞, 10]
  • (10、20)
  • (20、30)
  • (30, ∞)

InnoDB のデフォルトのトランザクション分離レベルは、繰り返し読み取り (RR) です。この場合、InnoDB は一時的なキー ロックを使用してファントム読み取りを防止します。

ファントム リードについて簡単に説明すると、トランザクション内で 2 つのクエリを実行します。最初のクエリは 5 つのデータ項目を返しますが、2 番目のクエリは 7 つのデータ項目を返します。これがファントム リードです。

これまでの多くのブログやインタビュー記事で、InnoDB の RR トランザクション分離レベルによってファントム リードを防止できることを学んだことがあるかもしれません。RR でファントム リードを防止する鍵となるのは、一時的なキー ロックです。

たとえば、学生テーブルにそれぞれ ID が 90 と 110 の 2 つの行があるとします。

SELECT * FROM student WHERE id > 100 FOR UPDATE;

この SQL ステートメントを実行すると、InnoDB は間隔 (90, 110] と (110,∞) にギャップ ロックを追加し、 id=110 のインデックスにレコード ロックを追加します。このようにして、100 がまったく存在しない場合でも、他のトランザクションはこの間隔に新しいデータを追加できません。

意図ロックを挿入

次は、 INSERTステートメントを実行する前に追加される挿入意図ロックです。本質的にはギャップロックの一種です。

もう一度例を見てみましょう。現在、インデックス レコード 10 と 20 があるとします。トランザクション A と B は、それぞれインデックス値 14 と 16 のデータを挿入します。このとき、トランザクション A と B は挿入意図ロックを使用して、10 と 20 の間のギャップをロックします。挿入意図ロックを取得した後、14 と 16 の排他ロックを取得します。

この時点では、トランザクション A と B は異なる行を挿入するため、互いにブロックされることはありません。

自動増分ロック

最後に、AUTO-INC ロックがあります。AUTO-INC ロックの本質は、非常に特殊なテーブル ロックです。トランザクション A がAUTO_INCREMENT列を含むテーブルにデータを追加すると、自動増分ロックが保持されます。このとき、他のトランザクション B は、トランザクション A が途中でギャップなく連続的に自己増分を取得することを確認するために待機する必要があります。

さて、以下のリンクから、基本概念の分析と RocketMQ の詳細なソースコード分析を含む MQ 学習教材を入手してください。継続的に更新されるため、この学習教材をお見逃しなく。

http://xiazai.jb51.net/202105/yuanma/RocketMQ_jb51.rar (必ず収集してください)

上記は、MySQL の関連ロックの詳細についての簡単な紹介です。MySQL ロックの詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • MySQL 悲観的ロックと楽観的ロックの実装
  • MySQLのロック機構に関する最も包括的な説明
  • MySQLのさまざまなロックに関する詳細な理解

<<:  ボタンのタイプが送信として指定されていません。ボタンをクリックしても、指定された URL にジャンプしません。

>>:  ミニマルなウェブサイトデザインの例

推薦する

フロントエンドの HTML 知識ポイントのまとめ (推奨)

1. HTMLの概要htyper テキスト マークアップ言語 ハイパーテキスト マークアップ言語ハ...

MySQL での varchar 型の日付の比較、並べ替え、その他の操作の実装

MySQL を使用する場合、日付は通常、datetime や timestamp などの形式で保存さ...

CSS マルチレベルメニュー実装コード

これは、Web ページを Windows のスタート メニューなどのデスクトップ プログラムのように...

IE8 ベータ 1 には注意が必要な 2 つの領域があります

<br />関連記事: Web スキル: 複数の IE バージョンを共存させるソリューシ...

MySQL学習エンジンの詳細な説明、説明、権限

エンジン導入InnodbエンジンInnodb エンジンは、データベース ACID トランザクションを...

珍しいけれど役に立つJSテクニックをいくつか紹介します

序文プログラミング言語には通常、さまざまな隠されたトリックが含まれており、これらのトリックを上手に使...

Dockerコンテナの構築と実行のプロセスの詳細な説明

イメージをプルし、コンテナを作成してコンテナを実行するだけです。 docker run -d --r...

jsを使用してカルーセル効果を実現する

今日は、参考までに、jsを使用してカルーセルマップの効果を実現する方法についてお話ししましょう。具体...

プライベートイメージウェアハウスを構築するためのDockerレジストリの実装方法

マイクロサービスのイメージは、保存用に Docker リポジトリにアップロードされます。一般的に使用...

Vue+Element+Springboot画像アップロードの実装例

最近、たまたま vue+springboot のフロントエンドとバックエンドの分離プロジェクトに触れ...

Enterキーを押すとフォームが自動的に送信されます。予期せぬ発見

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

Apache での ModSecurity のインストール、有効化、および構成

ModSecurity は、Web サーバーに入るすべてのパケットをチェックする強力なパケット フィ...

MySQL でのワイルドカードを使用したファジークエリの実装に関する簡単な説明

MySQL データベースでは、あいまいクエリが必要な場合にワイルドカードを使用します。まず、演算子と...

Vue スキャフォールディング学習プロジェクト作成方法

1. 足場とは何ですか? 1. Vue CLI Vue CLI は、Vue.js をベースにした迅速...

ネイティブ js はカスタム スクロール バー コンポーネントを実装します

この記事の例では、カスタムスクロールバーコンポーネントを実装するためのjsの具体的なコードを参考まで...