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 にジャンプしません。

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

推薦する

CSS3アニメーションジャミングソリューションについての簡単な説明

なぜ詰まっているのでしょうか?言及しなければならない前提があります。フロントエンド開発者は皆、ブラウ...

JavaScript を使用してテーブル情報を追加および削除する

JavaScript 入門JavaScript は軽量なインタープリタ型の Web 開発言語です。言...

位置固定オフセット問題を解決する方法の詳細な説明

質問CSS 固定配置の position:fixed は非常に使いやすいです。ブラウザのビューポート...

Linux 圧縮ファイルコマンド zip の使用例

「.zip」形式は、Windows システムでファイルを圧縮するために使用されます。実際、「.zip...

SQLはROW_NUMBER() OVER関数を使用してシーケンス番号を生成します。

構文: ROW_NUMBER() OVER(PARTITION BY COLUMN ORDER BY...

Javascript ファイルと Blob の詳細な説明

目次ファイル()文法パラメータ例ブロブ()文法パラメータ財産方法例要約するファイル() File()...

Swiper.jsプラグインを使用すると、カルーセル画像を非常に簡単に実装できます。

Swiper は、携帯電話やタブレットなどのモバイル端末向けに設計された、純粋な JavaScri...

Dockerを使用してGitlabを素早くデプロイする方法

1. GitLabイメージをダウンロードする docker pull gitlab/gitlab-c...

MAC で Mysql5.7.10 のルートパスワードを変更する方法

まず、MySQLをskip-grant-tablesモードで起動します: mysqld --skip...

Tomcat プロセスの CPU 使用率が高い場合のトラブルシューティング記録を記録する

この記事では主にTomcatプロセスを記録し、TCP接続が多すぎることによるCPU使用率の過剰のトラ...

AES_ENCRYPT() と AES_DECRYPT() を使用して MySQL を暗号化および復号化する正しい方法の例

序文最近、仕事でAES_ENCRYPT()関数を使用してプレーンテキストを暗号化し、MySQL に保...

CSSを使用してAndroidシステムの読み込みアニメーションを実装する

Web には一般的な読み込みアイコンが 2 つあります。1 つは iOS の「菊」、もう 1 つは ...

CSS3は、大きな円のドット分布と回転効果を実現するためにtransform-originを使用します。

まず、transform-origin 属性を使用する必要があります。transform 属性は必ず...

Reactは動的ポップアップウィンドウコンポーネントを実装します

UI コンポーネントを作成するときに、アニメーションを考慮しなければ、アニメーションを実現するのは非...

Webデザインの経験: Webコードを効率的に書く

本来、この第 7 章では、デザインにおけるレイヤーと空間テクニックについて深く議論するはずです。しか...