MySQLのロック機構の詳細解説 - テーブルロックと行ロック

1. データベースロック理論

ロックは、コンピュータが複数のプロセスまたはスレッドによるリソースへの同時アクセスを調整するメカニズムです。

データベースでは、CPU、RAM、I/O などの従来のコンピューター リソースの競合に加えて、データは多くのユーザーによって共有されるリソースでもあります。同時データ アクセスの一貫性と有効性をどのように確保するかは、すべてのデータベースが解決しなければならない問題です。さらに、ロックの競合もデータベースへの同時アクセスのパフォーマンスに影響を与える重要な要素です。

タオバオに商品を買いに行きましたが、在庫は 1 つだけです。この時点で他の人がそれを購入した場合、自分が買うか他の人が買うかという問題はどのように解決すればよいでしょうか? ここでのトランザクションは、まず在庫テーブルから商品数量を取り出し、次に注文を挿入し、支払い後に支払テーブル情報を挿入して、商品数量を更新します。このプロセスでは、ロックを使用することで限られたリソースを保護し、分離と同時実行の問題を解決できます。

2. ロックの分類

2.1 データ操作の種類による分類

ロックの分類は、データ操作のタイプに応じて読み取りロックと書き込みロックに分類されます。

  • 読み取りロック(共有ロック、Share Lock):同じデータに対して、複数の読み取り操作を相互に影響を与えることなく同時に実行できます。トランザクション T がデータ オブジェクト A に読み取りロックを追加すると、トランザクション T は A の読み取りのみ可能、他のトランザクションは A に読み取りロックを追加することのみが可能ですが、書き込みロックを追加することはできませんトランザクション T が A の読み取りロックを解放するまで。これにより、他のトランザクションは A を読み取ることができますが、トランザクション T が A の読み取りロックを解放するまで、A に変更を加えることができなくなります。

  • 書き込みロック (排他ロック、排他ロック): 追加できる書き込みロックは 1 つだけです。現在の書き込み操作が完了する前に、他の書き込みロックと読み取りロックがブロックされます。トランザクション T がデータ オブジェクト A に書き込みロックを追加した場合、トランザクション T のみが許可されます読んで修正するあ、T が A のロックを解放するまで、他のトランザクションは A にいかなる種類のロックも追加できませんこれにより、トランザクションの終了時にリソースの元のロックが解放されるまで、他のトランザクションがリソースのロックを取得できなくなります。INSERT、UPDATE 或 DELETE排他ロックは更新操作中に常に適用されます ( )。

2.2 データ操作による粒度分類

リレーショナル データベースでは、データ操作の粒度に応じて、テーブル ロック、行ロック、ページ ロックに分割されます。テーブル ロック、行ロック、ページ ロックは次のように比較されます。

  • テーブル ロック: MySQL で最も大きなロック粒度を持つロック。現在操作されているテーブル全体をロックすることを意味します。実装が簡単で、リソースの消費が少なく、ほとんどの MySQL エンジンでサポートされています。最も一般的に使用される MyISAM と InnoDB はどちらもテーブル レベルのロックをサポートしています。テーブルレベルのロックは、テーブル共有読み取りロック (共有ロック) とテーブル排他的書き込みロック (排他的ロック) に分けられます。

特徴: テーブルロックのオーバーヘッドが小さく、高速ロック、デッドロックなし、ロック粒度が大きい、ロック競合の可能性が最も高く、同時実行性が最も低い

  • 行ロック: MySQL で最も細かいロック粒度を備えたロックの一種。これは、現在操作されている行のみがロックされることを意味します。行レベルのロックにより、データベース操作における競合を大幅に減らすことができます。ロックの粒度は最も小さくなりますが、ロックのオーバーヘッドも最大になります。行レベルのロックは、共有ロックと排他ロックに分類されます。InnoDB ストレージ エンジンは、デフォルトで行ロックを使用します。InnoDB と MyISAM の間には 2 つの大きな違いがあります: 1 つはトランザクション (TRANSACTION) をサポートすること、もう 1 つは行レベルのロックを使用することです

特徴: 行ロックは追加にコストがかかり、時間がかかります。デッドロックが発生する可能性があります。ロックの粒度は最も小さく、ロックの競合の可能性は最も低く、同時実行性も最も高くなります。

  • ページ ロック: ページ ロックは、ロック粒度が行レベルのロックとテーブル レベルのロックの間である MySQL のロックです。テーブルレベルのロックは高速ですが、競合が多く、行レベルのロックは競合が少ないですが、遅くなります。したがって、隣接するレコードのグループを一度にロックするために、妥協したページ レベルが採用されます。

特徴: オーバーヘッドとロック時間はテーブル ロックと行ロックの間であり、デッドロックが発生します。ロックの粒度はテーブル ロックと行ロックの間で、同時実行性は平均的です。

3. テーブルロックの適用

テーブル ロックは、現在操作されているテーブル全体をロックするため、実装が簡単です。リソースの消費も比較的少なく、ロックは高速であり、デッドロックは発生しません。ロック競合が発生する可能性は最も高く、同時実行性の度合いは最も低くなります。MyISAM エンジンと InnoDB エンジンは両方ともテーブル レベルのロックをサポートします。MyISAM ストレージ エンジンでは、select共有ロックがステートメントに自動的に追加され、update/delete/insert排他ロックが操作に追加されます。

3.1 テーブルロック関連コマンド

(1) テーブルロックを手動で追加する

lock table 表名字 read(write),表名字2 read(write)

(2) テーブルに追加されたロックのコマンドを表示する

show open tables

ここに画像の説明を挿入

(3) テーブルロックを解除するコマンド

unlock tables

(4) テーブルロック解析用コマンド

 show status  like 'table%';

ここに画像の説明を挿入
table_locks_waited状態変数を検査することで、table_locks_immediateシステムのテーブル ロックの状況を分析できます。2 つの変数の説明は次のとおりです。

  • table_locks_waited:ロックをすぐに取得できないことと、ロックを待つ回数を示します。
  • table_locks_immediate:即時にロックを取得できる回数

table_locks_waited値が比較的高い場合は、テーブルレベルの深刻なロック競合が存在することを意味します。現時点では、アプリケーションをさらにチェックして問題を特定する必要があります。

3.2 テーブル共有読み取りロックをテーブルに追加する

テーブル共有読み取りロック。共有読み取りロックのあるテーブルは、他のセッションの読み取りリクエストをブロックしませんが、他のセッションの書き込みリクエストをブロックします。

データを作成する SQL:

create table mylock (
id int not null primary key auto_increment,
name varchar(20) default ''
) engine myisam;

insert into mylock(name) values('a');
insert into mylock(name) values('b');
insert into mylock(name) values('c');
insert into mylock(name) values('d');
insert into mylock(name) values('e');
### 3.1 读锁

mylock テーブルに読み取りロックを追加するには (読み取りブロック書き込みの例)
ここに画像の説明を挿入

ここに画像の説明を挿入
要約:

共有読み取りロックを持つテーブルは、他のセッションの読み取り (選択) リクエストをブロックしませんが、現在のセッションと他のセッションの書き込み (挿入、更新、削除) リクエストをブロックします。

3.3 テーブルに排他的書き込みロックを追加する

排他的書き込みロックは、誰もがよく知っている排他的ロックです。他のプロセスによる同じテーブルの読み取りおよび書き込み操作がブロックされます。現在の排他的ロックが解放された場合にのみ、他のプロセスの読み取りおよび書き込み操作が実行されます。
ここに画像の説明を挿入
書き込みロックを追加すると、現在のセッションは他のテーブルの読み取りと書き込みができなくなりますが、他のセッションは他のテーブルの読み取りと書き込みが可能になります。
ここに画像の説明を挿入

3.4 インテント共有ロックとインテント排他ロック

トランザクションが必要な特定のリソースをロックする必要がある場合、必要なリソースをロックしている共有ロックに遭遇すると、別の共有ロックを追加できますが、排他ロックを追加することはできません。ただし、ロックする必要があるリソースがすでに排他ロックによって占有されている場合は、ロックされたリソースを取得して独自のロックを追加する前に、ロックがリソースを解放するのを待つしかありません。

InnoDB は複数粒度のロックをサポートしており、行ロックとテーブル ロックを共存させることができますインテンション ロックはテーブル レベルのロックです。インテンション ロックの目的は、後でトランザクションがテーブル内の行に対してどのタイプのロック (共有ロックまたは排他ロック) を使用する必要があるかを示すことです。つまり、トランザクションがリソース ロックを取得する必要がある場合、必要なリソースがすでに排他ロックによって占有されている場合、トランザクションは行がロックされているテーブルに適切なインテンション ロックを追加する必要がある可能性があります。自分で共有ロックが必要な場合は、インテント共有ロックをテーブルに追加します。また、特定の行 (または一部の行) に排他ロックを追加する必要がある場合は、まずテーブルに意図的な排他ロックを追加します。複数のインテント共有ロックは同時に共存できますが、同時に存在できるインテント排他ロックは 1 つだけです。

InnoDB ストレージ エンジンの 2 つのテーブル レベルのロック:

  • 意図共有ロック (IS): トランザクションがデータ行の共有ロックを記録しようとしていることを示します。トランザクションは、データ行に共有ロックを追加する前に、まずテーブルの IS ロックを取得する必要があります。

  • インテント排他ロック (IX): トランザクションがデータ行に排他ロックを追加しようとしており、トランザクションはデータ行に排他ロックを追加する前に、まずテーブルの IX ロックを取得する必要があることを意味します。

知らせ:

  • ここでのインテント ロックはテーブル レベルのロックです。これはトランザクションがレコードの特定の行を読み書きしていることを意味するだけの意図を表し、競合があるかどうかは実際に行ロックが追加されるときに判断されます。インテンション ロックは、ユーザーの介入なしに InnoDB によって自動的に追加されます。
  • IX、IS はテーブル レベルのロックであり、行レベルの X、S ロックとは競合しませんが、テーブル レベルの X、S ロックとのみ競合します。

InnoDB のロック メカニズムの互換性を次の図に示します。
ここに画像の説明を挿入

トランザクションによって要求されたロック モードが現在のロックと互換性がある場合、InnoDB は要求されたロックをトランザクションに許可しますが、互換性がない場合、トランザクションはロックが解放されるまで待機します。

3.5 同時挿入

読み取り/書き込みロック間の競合を減らすために、MyISAM ストレージ エンジンは同時挿入をサポートしています。local キーワードを使用してテーブルに読み取りロックを追加できますが、他のセッションは引き続きテーブルに対して書き込み操作を実行できます。ただし、現在読み取りロックを保持しているセッションが読み取りロックを解放した場合にのみ、他のセッションの書き込み操作の結果が表示されます。構文は次のとおりです。

lock table 表名 read local

このように、現在のテーブルが読み取りロックされている場合、他のセッションはテーブルにレコードを追加できますが、そのレコードはグローバル変数とともに使用する必要がありますconcurrent_insertMySQL パラメータのconcurrent_insert列挙値と意味は次のとおりです。

  • NEVER : 読み取りロックを追加した後、他のセッションは同時に書き込みできなくなります。
  • AUTO : 読み取りロックが追加された後、テーブルにホールがない (つまり、行が削除されていない) という条件下で、他のセッションは同時に書き込みを許可されます。
  • ALWAYS : 読み取りロックを追加すると、穴のあるテーブルであっても、他のセッションは同時に書き込みを許可されます。

現在のデータベースの設定を表示します。

show global variables like '%concurrent_insert%';

ここに画像の説明を挿入

データベース設定を変更します。

set global concurrent_insert = ALWAYS;

ここに画像の説明を挿入

3.6 MyISAM ロック スケジュール メカニズム

テーブル レベルのロックのみを使用するストレージ エンジン (MyISAM、MEMORY、MERGE など) の場合、書き込みプロセスの優先順位は読み取りプロセスよりも高くなります。読み取りプロセスはキューの先頭にありますが、書き込みプロセスはキューの先頭にあります。プロセスもキューに入れられます。システム変数 low-priority-updates=1 を設定すると、すべてのステートメントは、INSERT、UPDATE、DELETE和 LOCK TABLE WRITE影響を受けるテーブルで保留中の操作SELECTまたはLOCK TABLE読み取り操作がなくなるまで待機します。

ここに画像の説明を挿入

3.7 概要

  • 共有読み取りロックには互換性がありますが、共有読み取りロックと排他的書き込みロック、および排他的書き込みロックは相互に排他的です。つまり、読み取りと書き込みはシリアルです。
  • MyISAM では、特定の条件下でクエリと挿入を同時に実行できるため、これを使用して、アプリケーション内の同じテーブルに対するクエリと挿入のロック競合の問題を解決できます。
  • MyISAM のデフォルトのロック スケジュール メカニズムは書き込み優先ですが、必ずしもすべてのアプリケーションに適しているわけではありません。ユーザーはLOW_PRIORITY_UPDATESパラメータを設定するか、INSERT、UPDATE、DELETEステートメントでオプションを指定することによってLOW_PRIORITY、読み取り/書き込みロックの競合を調整できます。
  • テーブル ロックのロック粒度が大きいことと、読み取りと書き込みの間のシリアル化により、多くの更新操作がある場合、MyISAM テーブルで深刻なロック待機が発生する可能性があります。ロックの競合を減らすために、InnoDB ストレージ エンジンの使用を検討できます。

4. 行ロックの適用

4.1 基本的な紹介

InnoDB ストレージ エンジンは、デフォルトで行ロックを使用します。行レベルのロックは、ロックの粒度が最も小さく、ロック競合の可能性が最も低く、同時実行性が最も高くなります。ただし、行ロックのコストが高く、ロックが遅く、デッドロックが発生します。InnoDB はインデックスに基づいた行ロック、例えば:

select * from tab_with_index where id = 1 for update;

for update条件に応じて行ロックが可能で、IDはインデックスキーを持つ列で、ID がインデックス キーでない場合、InnoDB はテーブル ロックを実行します。

InnoDB と MyISAM の間には 2 つの大きな違いがあります。1. トランザクションをサポートする 2. 行レベルのロックを使用する

MySQL トランザクションは、一連の SQL ステートメントで構成される論理処理単位であり、トランザクションの ACID 特性と呼ばれる次の 4 つの属性を持ちます。

  1. アトミック性: トランザクションは実行の最小単位であり、分割することはできません。トランザクションのアトミック性により、アクションが完全に完了するか、まったく影響がないことが保証されます。
  2. 一貫性 (一貫性): トランザクションの実行前と実行後、データは一貫した状態を維持する必要があり、同じデータを読み取る複数のトランザクションの結果は同じです。
  3. 分離: データベース システムは特定の分離メカニズムを提供し、データベースに同時にアクセスする場合、ユーザーのトランザクションは他のトランザクションによって妨害されず、同時トランザクション間のデータベースは独立しています。
  4. 耐久性: トランザクションがコミットされた後。データベース内のデータに対する変更は永続的であり、データベースに障害が発生した場合でも、データベースに影響を与えることはありません。

一般的なアプリケーションでは、複数のトランザクションが同時に実行され、多くの場合、同じデータを操作してそれぞれのタスクを完了します (複数のユーザーが
同じデータを操作します)。同時実行性は必要ですが、次のような問題が発生する可能性があります。

  1. ダーティ リード: トランザクションがデータにアクセスしてデータを変更しており、この変更がデータベースに送信されていない場合、別のトランザクションもデータにアクセスし、このデータを使用します。このデータはコミットされていないデータであるため、別のトランザクションによって読み取られたデータは「ダーティデータ」となり、「ダーティデータ」に基づく演算は正しく行われない可能性があります。
  2. 変更不可 (変更不可): 1 つのトランザクションがデータを読み取ると、別のトランザクションもそのデータにアクセスし、最初のトランザクションでデータが変更された後、2 番目のトランザクションもこのデータを変更します。このように、最初のトランザクションでの変更結果が失われるため、変更ロストと呼ばれます。
    たとえば、トランザクション 1 はテーブル内のデータ A=20 を読み取り、トランザクション 2 も A=20 を読み取り、トランザクション 1 は A=A-1 を変更し、トランザクション 2 も A=A-1 を変更します。最終結果は A=19 です。 1 件の変更が失われます。
  3. 反復不可能な読み取り (Unrepeatable read): トランザクション内で同じデータを複数回読み取ることを指します。このトランザクションが終了していない間、別のトランザクションもデータにアクセスします。その後、最初のトランザクションの 2 つの読み取りの間で、2 番目のトランザクションの変更により、最初のトランザクションで読み取られたデータが異なる可能性があります。これは、トランザクション内で 2 回読み取られたデータが異なる場合に発生するため、Non-Repeatable Read と呼ばれます。
  4. ファントム読み取り: ファントム読み取りは、非反復読み取りと似ています。これは、1 つのトランザクション (T1) が数行のデータを読み取り、別の同時トランザクション (T2) がデータを挿入したときに発生します。後続のクエリでは、最初のトランザクション (T1) は、あたかも幻覚が起こったかのように、存在しないレコードをさらにいくつか検出するため、ファントム リーディングと呼ばれます。

非反復読み取りとファントム読み取りの違い: 非反復読み取りの焦点は、レコードを複数回読み取って一部の列の値が変更されたことを検出するなど、変更に焦点を当てます。ファントム読み取りの焦点は追加または削除です。 、複数回の読み取りなど、レコードの増加または減少が見つかったレコード。

MySQL には 4 つのトランザクション分離レベルがあります。

  • READ-UNCOMMITTED (コミットされていない読み取り): コミットされていないデータ変更の読み取りを許可する最も低い分離レベル。これにより、ダーティ リード、ファントム リード、または反復不可能な読み取りが発生する可能性があります。
  • READ-COMMITTED (コミットされた読み取り): 同時トランザクションによってコミットされたデータを読み取ることができます。これによりダーティ リードを防ぐことができますが、ファントム リードまたは反復不可能な読み取りが発生する可能性があります。
  • REPEATABLE-READ (反復可能読み取り): 同じフィールドの複数の読み取りの結果は、データが独自のトランザクションによって変更されない限り一貫しています。これにより、ダーティ リードや非反復読み取りを防ぐことができますが、ファントム読み取りは依然として可能です。
  • SERIALIZABLE (シリアル化可能): ACID 分離レベルに完全に準拠した最高の分離レベル。すべてのトランザクションは 1 つずつ実行されるため、トランザクション間の干渉の可能性はありません。つまり、このレベルでは、ダーティ リード、反復不可能なリード、ファントム リードを防ぐことができます。

MySQL InnoDB ストレージ エンジンデフォルトでサポートされる分離レベルは REPEATABLE-READ (反復読み取り) です。

4.2 行ロックの使用

テーブル SQL を作成します。

CREATE TABLE test_innodb_lock (
a INT(11),
b VARCHAR(16)
)ENGINE=INNODB;

insert into test_innodb_lock values(1,'b2');
insert into test_innodb_lock values(2,'2000');
insert into test_innodb_lock values(3,'3000');
insert into test_innodb_lock values(4,'4000');
insert into test_innodb_lock values(5,'5000');
insert into test_innodb_lock values(6,'6000');
insert into test_innodb_lock values(7,'7000');

create index idx_a on test_innodb_lock(a);
create index idx_b on test_innodb_lock(b);

通常の select ステートメントはレコードをロックしません。クエリ中にレコードに行ロックを追加する場合は、次の 2 つの方法を使用できます。

#对读取的记录加共享锁
select... from ... where ... lock in share mode;

#对读取的记录加独占锁
select ... from ... where ... for update;

上記の 2 つのステートメントはトランザクション内に存在する必要があります。トランザクションがコミットされるとロックが解放されるためです。そのため、これら 2 つのステートメントを使用する場合は、begin、starttransaction を追加するか、自動送信をキャンセルする必要があります。
自動コミットコマンドをキャンセルするには:

set autocommit = 0;

行ロックのデモ:
更新のために行をロックする
ここに画像の説明を挿入

2 つのセッションが同じ行を更新すると、セッション 2 はブロックされます。
ここに画像の説明を挿入

2 つのセッションは異なる行を更新し、セッション 1 は a=1 を更新し、セッション 2 は他の行を更新します。これらはブロックせずに通常通り更新できます。
ここに画像の説明を挿入
知らせ:インデックスが有効でない場合、またはクエリ条件にインデックスが作成されていない場合、行ロックによってテーブル ロックが変更されます。たとえば、varchar が使用されていない場合、システムは自動的に型を変換し、インデックスを無効にします。

インデックスに障害が発生した場合、行ロックはテーブル ロックになり、セッション 1 が保持しているテーブル ロックが解放されるまで、セッション 2 の更新操作はブロックされます。
ここに画像の説明を挿入
インデックスが無効になっていない場合、行ロックを使用してもセッション 2 の更新操作はブロックされません。
ここに画像の説明を挿入

4.3 行ロックのアルゴリズム

InnoDB ストレージ エンジンの行ロックには 3 つのアルゴリズムがあります。

  • レコードロック: インデックス項目をロックし、条件を満たす行をロックします。他のトランザクションはロックされたアイテムを変更したり削除したりすることはできません。
  • ギャップ ロック:インデックス アイテム間の「ギャップ」をロックし、インデックス アイテム自体を除いたレコードの範囲をロックします (最初のレコードの前のギャップまたは最後のレコードの後のギャップをロックします)。他のトランザクションはロック範囲内にデータを挿入できないため、他のトランザクションがファントム行を追加することはできません。
  • ネクストキーロック: インデックスエントリ自体とインデックス範囲をロックします。それは、Record LockGap Lockの組み合わせです。ファントムリーディングの問題を解決できます。

4.3.1 レコードロック

Innodb は行クエリにNext-key Lockを使用します。Next-key Lock は、ファントム問題のファントム読み取り問題を解決するために使用されます。

クエリされたインデックスに一意の属性が含まれている場合は、Next-key Lock を Record Lock にダウングレードします。次の SQL:

SELECT id FROM user WHERE id = 1;

id 列が唯一のインデックス列の場合、id=1 のインデックス レコードがロックされ、このときレコード ロックが使用されます。

4.3.2 ギャップロック

ギャップ ロックは、インデックス レコード間のギャップに対するロック、または最初のインデックス レコードの前または最後のインデックス レコードの後のギャップに対するロックです。

ここに画像の説明を挿入
列 a の現在の範囲 (1,5) のギャップがギャップによってロックされているため、値 2 を test_innnodb_lock テーブルの列 a に挿入できないことがわかります。

等価条件ではなく範囲条件を使用してデータを取得し、共有ロックまたは排他ロックをリクエストすると、InnoDBは、条件を満たす既存のデータレコードのインデックス項目をロックし、キー値が条件範囲内であっても存在しないレコードのことを「ギャップ(GAP)」と呼びます。InnoDB はこの「ギャップ」もロックします。このロック機構はいわゆるギャップ ロック (Next-Key ロック) です。

ギャップ ロックには比較的致命的な弱点があります。つまり、ある範囲のキー値をロックした後、一部の存在しないキー値もロックされてしまうため、ロック中にロックされたキー値の範囲内にデータを挿入することができなくなります。一部のシナリオでは、これはパフォーマンスに非常に悪影響を与える可能性があります。

ギャップ ロック設計の目的は、ファントム読み取りの問題を引き起こす可能性がある、複数のトランザクションが同じ範囲にレコードを挿入することを防ぐことです。
Gap Lock ギャップ ロックを明示的に閉じるには 2 つの方法があります: (外部キー制約と一意性チェックを除き、他の場合にはレコード ロックのみが使用されます)。

  • トランザクション分離レベルをREAD-COMMITTED (読み取りコミット)に設定します。
  • パラメータinnodb_locks_unsafe_for_binlogを 1 に設定します

4.3.3 ネクストキーロック

ネクストキーロックは、インデックス項目自体とインデックス範囲をロックします。つまり、レコードロックとギャップロックの組み合わせにより、ファントム読み取りの問題を解決できます。

例: トランザクション T1 が r 行に共有ロックまたは排他ロックを追加すると、r 行の前のギャップにギャップ ロックも追加されます。このとき、別のトランザクション T2 は r 行の前に新しいインデックス レコードを挿入できません。
インデックスに値 10、11、13、20 が含まれているとします。このインデックスの可能な Next-Key ロックは、次の間隔をカバーします。

(-∞, 10]
(10, 11]
(11, 13]
(13, 20]
(20, +∞))
最後の間隔では、Next-Key Locks はインデックスの最大値を超えるギャップと「正の無限大」をロックします。 " 擬似レコード。擬似レコードの値は、インデックス内の実際の値よりも高くなります。これは実際のインデックス レコードではないため、実際には、この Next-Key Locks は最大インデックス値の後のギャップのみをロックします。

デフォルトでは、InnoDB はREPEATABLE READトランザクション分離レベルで動作します。この場合、InnoDB は検索とインデックス スキャンに Next-Key ロックを使用し、ファントム読み取りを防ぎます。

行ロックの詳細: https://mp.weixin.qq.com/s/1LGJjbx_n_cvZndjM3R8mQ

4.4 行ロック競合の分析

InnoDB_row_lock ステータス変数をチェックしてシステム上の行ロックの競合を分析するには、コマンドは次のとおりです。

 show status like 'innodb_row_lock%';

ここに画像の説明を挿入
各ステータス量の説明は次のとおりです。

  • innodb_row_lock_current_waits: 現在ロックを待っている番号。
  • innodb_row_lock_time:システム起動から現在までの総ロック時間(総待ち時間)
  • innodb_row_lock_time_avg:毎回の平均待ち時間(平均待ち時間)
  • innodb_row_lock_time_max:システム起動から現在までの最長イベント待ち
  • innodb_row_lock_waits:システム起動からの総待ち時間(総待ち時間数)

待ち時間が多く、毎回の待ち時間が少なくない場合は、なぜシステム内で待ち時間が多くなるのかを分析し、その分析結果に基づいて最適化計画を策定する必要があります。

4.5 デッドロックとデッドロックの回避

デッドロックとは、2 つ以上のトランザクションが同じリソースを占有し、お互いのリソースのロックを要求し、悪循環が発生する現象です。

InnoDB の行レベルのロックはインデックスに基づいて実装されます。クエリ ステートメントがどのインデックスにもヒットしない場合、InnoDB はテーブル レベルのロックを使用します。.. さらに、InnoDB の行レベルのロックはデータ レコードではなくインデックスに対してロックされるため、異なる行のレコードにアクセスした場合でも、同じインデックス キーが使用されている場合はロックの競合が発生します。

また、一度に必要なロックを常に取得する MyISAM とは異なり、InnoDB のロックは段階的に取得されるため、2 つのトランザクションが相手が保持しているロックを取得する必要がある場合、双方が待機することになり、デッドロックが発生します。デッドロックが発生した後、InnoDB は通常、それを検出し、1 つのトランザクションにロックを解放してロールバックさせ、もう 1 つのトランザクションはロックを取得してトランザクションを完了させることができます。

  • テーブルレベルのロックを使用して、デッドロックの可能性を減らしますデッドロックが非常に発生しやすいビジネス部分については、アップグレードされたロック粒度を使用して、テーブルレベルのロックを通じてデッドロックの可能性を減らすことができます。
  • 複数のプログラムが同じ順序でテーブルにアクセスすることに同意しようとします異なるプログラムが複数のテーブルに同時にアクセスする場合は、同じ順序でテーブルにアクセスすることに同意するようにしてください。これにより、デッドロックの可能性を大幅に減らすことができます。
  • 同じトランザクションは、必要なすべてのリソースを一度にロックしようとする必要があり、デッドロックの可能性を減らすことができます。

4.6 概要

InnoDB ストレージ エンジンは行レベルのロックを実装しているため、ロック メカニズムの実装によって生じるパフォーマンスの損失はテーブル レベルのロックよりも高くなる可能性がありますが、全体的な同時処理の点では MyISAM のテーブル レベルのロックよりもはるかに優れています。能力の。システムの同時実行性が高い場合、InnoDB の全体的なパフォーマンスは MyISAM と比較して明らかな利点があります。

ただし、InnodbDB の行レベル ロックには脆弱な側面もあり、これを不適切に使用すると、InnoDB の全体的なパフォーマンスが MyISAM よりも高くなるだけでなく、さらに悪くなる可能性があります。

InnoDB ストレージ エンジンを使用するための最適化の提案:

  1. 可能な限り、すべてのデータ取得はインデックスを通じて行われ、インデックスのない行ロックがテーブル ロックにアップグレードされるのを回避します。
  2. ロックの範囲を最小限に抑えるためにインデックスを合理的に設計する
  3. ギャップロックを避けるために検索条件をできるだけ少なくする
  4. トランザクション サイズを制御し、ロックされているリソースの量と時間を減らしてみてください。
  5. 可能な限り低レベルのトランザクション分離

5. ページロック

ページ ロックは MySQL のロックの一種で、そのロック粒度は行レベルのロックとテーブル レベルのロックの間です。テーブルレベルのロックは高速ですが、競合が多く、行レベルのロックは競合が少ないですが、遅くなります。ページ レベルで妥協が行われ、連続するレコードのセットが一度にロックされます。BDB ストレージ エンジンはページレベルのロックをサポートします。オーバーヘッドとロック時間はテーブル ロックと行ロックの間にあり、デッドロックが発生します。ロックの粒度はテーブル ロックと行ロックの間であり、同時実行性は平均的です。

6. 分離レベルとロックの関係

Read Uncommittedレベルでは、データの読み取りに共有ロックは必要ないため、変更されたデータの排他ロックと競合しません。

Read Committedレベルでは、読み取り操作で共有ロックを追加する必要がありますが、共有ロックはステートメントの実行後に解放されます。

反復可能読み取りレベルでは、読み取り操作で共有ロックを追加する必要がありますが、トランザクションがコミットされる前に共有ロックは解放されません。つまり、共有ロックはトランザクションの完了後に解放される必要があります。

SERIALIZABLE は、キーの範囲全体をロックし、トランザクションが完了するまでロックを保持するため、最も制限の厳しい分離レベルです。

7. データベースの楽観的ロックと悲観的ロック

データベース管理システム (DBMS) における同時実行制御のタスクは、複数のトランザクションがデータベース内の同じデータに同時にアクセスするときに、トランザクションの分離と統一性、およびデータベースの統一性が破壊されないようにすることです。楽観的同時実行制御 (オプティミスティック ロック) と悲観的同時実行制御 (ペシミスティック ロック) は、同時実行制御で使用される主な技術手段です。

  • 悲観的ロック: 同時実行性の競合が発生することを想定し、データの整合性に違反する可能性のあるすべての操作がブロックされます。データがクエリされると、トランザクションはコミットされるまでロックされます。

悲観的ロックの実装: データベースのロック メカニズムを使用する

  • オプティミスティック ロック: 同時実行性の競合が発生しないことを前提として、操作をコミットするときにのみデータ整合性違反をチェックします。データを変更する場合は、トランザクションをロックし、バージョンごとにロックします。

楽観的ロックの実装方法: 通常、バージョン番号メカニズムまたは CAS アルゴリズムを使用して実現します。

2 つのロックの使用シナリオについては、上記の 2 つのロックの紹介から、2 つのロックにはそれぞれ長所と短所があることがわかり、楽観的ロックが適しているなど、一方が他方よりも優れているとは考えられません。書き込みが少なくなる (複数読み取りシナリオ)、つまり競合がほとんど発生しない場合、ロックのオーバーヘッドが節約され、システム全体のスループットが向上します。

ただし、書き込みが多い場合は競合が頻繁に発生し、上位層のアプリケーションが再試行を繰り返すことになり、実際にパフォーマンスが低下するため、書き込みが多いシナリオでは悲観的ロックを使用する方が適切です。

8. まとめ

MyISAM および InnoDB ストレージ エンジンによって使用されるロック:

  • MyISAM はテーブルレベルのロックを使用します。
  • InnoDB は行レベルのロックとテーブルレベルのロックをサポートしており、デフォルトは行レベルのロックです。

テーブル レベルのロック、行レベルのロック、およびページ ロックの比較:

  • テーブル レベル ロック: Mysql で最も大きなロック粒度を持つロックです。現在操作されているテーブル全体をロックします。実装が簡単で、リソースの消費が少なく、迅速にロックされ、デッドロックが発生しません。ロックの粒度が最も大きく、ロック競合が発生する可能性が最も高く、同時実行性が最も低く、MyISAM エンジンと InnoDB エンジンの両方がテーブル レベルのロックをサポートしています。

特徴: 小さなオーバーヘッド、高速なロック、デッドロックなし、大きなロック粒度、ロック競合の可能性が最も高く、同時実行性が最も低い。

  • 行レベルのロック: Mysql でロック粒度が最も小さいロック。現在操作されている行に対してのみロックされます。行レベルのロックにより、データベース操作における競合を大幅に減らすことができます。ロックの粒度は最も小さく、同時実行性は高くなりますが、ロックのオーバーヘッドも最も大きく、ロックが遅くなり、デッドロックが発生する可能性があります。

特徴: 高いオーバーヘッド、遅いロック、デッドロックが発生する、最小のロック粒度、最小のロック競合確率、および最大の同時実行性。

  • ページレベルのロック: BDB ストレージ エンジンはページレベルのロックをサポートします。ロック粒度が行レベルのロックとテーブルレベルのロックの間である MySQL のロック。テーブルレベルのロックは高速ですが、競合が多く、行レベルのロックは競合が少ないですが、遅くなります。ページ レベルで妥協が行われ、連続するレコードのセットが一度にロックされます。

特徴: テーブルロックと行ロックの間にオーバーヘッドとロック時間があり、デッドロックが発生します。ロックの粒度はテーブル ロックと行ロックの間であり、同時実行性は平均的です。

上記の特性から、一般的にどのロックが優れているかを言うのは難しいことがわかりますが、特定のアプリケーションの特性にはどのロックがより適しているかがわかります。ロックの観点からのみ: テーブル レベルのロックは、インデックス条件に従って更新されるデータが少量のみのクエリベースのアプリケーションにより適していますが、行レベルのロックは、多数の同時更新が行われるアプリケーションにより適しています。インデックス条件に従って少量の異なるデータを処理し、同時にアプリケーションで同時クエリを実行します。

参考:
1. https://blog.csdn.net/qq_34337272/article/details/80611486
2. https://mp.weixin.qq.com/s/rFBFwzsDvoqptTubAqyuFQ
3. https://zhuanlan.zhihu.com/ p/123962424

おすすめ

転載: blog.csdn.net/huangjhai/article/details/119011417