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のwebkit-box-reflectを巧みに使用して、さまざまな動的効果を実現します。

かなり前の記事で、 -webkit-box-reflectプロパティについて説明しました。リフレクシ...

JavaScriptの原理と方向性

これが何を指しているのかをどのように判断するのでしょうか? ①グローバル環境で呼び出された場合はwi...

2列のデータをSQLの新しい列として操作する

以下のように表示されます。 bb_sbからa1、a2、a1+a2 a、a1*a2 b、a1*1.0/...

Vue2は応答性を提供するためにprovide injectを実装しています

1. vue2 での従来の書き方 // 親コンポーネントは 'foo' を提供します...

MacでのMySQL初期化パスワード操作

Macでデータベースを操作する際に個人が遭遇するデータベース起動の問題の簡単な記録1. Apple-...

MySQLはinet_atonとinet_ntoaを使用してIPアドレスデータを処理します。

この記事では、適切な形式を使用して IP アドレス データをデータベースに保存し、IP アドレスを簡...

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

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

Vue でユーザー権限に基づいてルートを動的に追加する方法

ユーザーの権限に応じて異なるメニュー ページを表示します。知識ポイントルートガード(事前ガードを使用...

VUE を使用して Ali Iconfont ライブラリをオンラインで呼び出す方法

序文何年も前、私はサーバー側の初心者でしたが、業界の競争が激しくなるにつれて、フロントエンドの初心者...

スパンの最小高さを定義するソリューションは効果がありません

span タグは HTML ウェブページを作成するときによく使用されますが、このタグの使い方がよくわ...

Docker はクラスター MongoDB 実装手順を構築します

序文会社の業務上のニーズにより、独自の MongoDB サービスを構築する予定です。MongoDB ...

EChartsマルチチャート連携機能の実装プロセス

表示するデータが多い場合、1 つのチャートに表示しても効果はよくありません。このとき、2 つのチャー...

React サーバーサイドレンダリング原則の分析と実践

ほとんどの人は、サーバーサイド レンダリング (SSR と呼んでいます) の概念について聞いたことが...

入力ボックスの値を取得する方法のReactの例

入力ボックスの値を取得する複数の方法最初の方法は、制御されていないコンポーネントの取得です2番目の方...

Linuxのファイルとフォルダの権限を操作する方法

Linux のファイル権限まず、現在のディレクトリ内のファイルの内容を確認しましょう。 ls -l ...