並行シナリオ
MySQLを学ぶとき、私たちはしばしば、物事の4つの特性、原子性、一貫性、分離、および耐久性について言及します。では、分離はどのように達成されますか?
分離の本質は、SQLステートメントがシリアルに実行される場合、同時実行性を制御することです。そうすれば、データベースの4つの特性に分離の概念がなくなり、ダーティ読み取り、繰り返し不可能な読み取り、ファントム読み取りなどの問題が発生しなくなります。
データベースには、書き込み-書き込み、読み取り-読み取り、読み取り-書き込み、書き込み-読み取りの4種類の同時操作しかありません。
書き込み-書き込み
トランザクションAがレコードを更新するとき、トランザクションBは同じレコードを同時に更新できますか?
答えは間違いなくノーです、さもなければそれは汚い書き込みの問題を引き起こします。汚い書き込みを避ける方法は?答えはロックすることです
読んで読んで
MySQLの読み取り操作はデフォルトではロックされていないため、並行して読み取ることができます
読み取り-書き込みおよび書き込み-読み取り
さまざまなシナリオでの同時操作のさまざまな許容レベルに基づいて、MySQLは分離の概念を開発しました。ビジネスシナリオに応じて分離レベルを選択できます。
√はそれが起こることを意味し、×はそれが起こらないことを意味します
分離レベル | ダーティリード | 繰り返し不可の読み取り | 幻覚 |
---|---|---|---|
コミットされていない読み取り | √ | √ | √ |
コミットされた読み取り | ×× | √ | √ |
繰り返し可能な読み取り | ×× | ×× | √ |
シリアライズ可能(シリアライズ可能) | ×× | ×× | ×× |
MySQLのロック
行レベルのロック
InnoDBストレージエンジンには、次の2種類の行レベルのロックがあります。
- 共有ロック(共有ロック、Sロックと呼ばれる)。トランザクションがレコードを読み取る必要がある場合、最初にSロックを取得してレコードを変更する必要があります。
- 排他ロック(排他ロック、Xロックと呼ばれる)。トランザクションがレコードを変更する場合、最初にレコードのXロックを取得する必要があります。
トランザクションT1がレコードのSロックを取得すると、トランザクションT2もこのレコードにアクセスします。トランザクションT2がこのレコードのSロックを再度取得する場合、成功する可能性があります。この状況はロック互換性と呼ばれます。トランザクションT2がこのレコードのXロックを再度取得する場合、トランザクションT1がSをコミットするまでこの操作はブロックされます。ロック解除
トランザクションT1がレコードのXロックを取得する場合、トランザクションT2が次にレコードのSロックまたはXロックを取得するかどうかに関係なく、トランザクション1がコミットするまでブロックされます。これはロック非互換性と呼ばれます。
複数のトランザクションが同時にレコードを読み取ることができます。つまり、共有ロックは相互に排他的ではありませんが、共有ロックは排他ロックをブロックします。排他ロック間の相互排除
SロックとXロックの互換関係は次のとおりです。
互換性 | Xロック | Sロック |
---|---|---|
Xロック | 相互に排他的 | 相互に排他的 |
Sロック | 相互に排他的 | 互換性 |
update、delete、insertは、関連するデータに排他ロックを自動的に追加し、selectステートメントはデフォルトでロックを追加しません。
どのような状況で読み取り操作がロックされますか?
- 選択…共有モードでロックし、読み取りレコードにSロックを追加します
- 更新するには...を選択し、読み取りレコードにXロックを追加します
- トランザクションでレコードを読み取り、読み取ったレコードにSロックを追加します
- トランザクション分離レベルはSERIALIZABLEの下にあり、読み取りレコードにSロックが適用されます
InnoDBの行ロックには3つのアルゴリズムがあります。
- レコードロック:単一のレコードをロックします
- ギャップロック:ギャップロック、レコードの前のギャップをロックし、レコードを挿入できないようにします
- Next-key Lock:データとデータの前のギャップを同時にロックします。つまり、データもデータの前のギャップもレコードを挿入できません。
デモンストレーション用のデモを書く
CREATE TABLE `girl` (
`id` int(11) NOT NULL,
`name` varchar(255),
`age` int(11),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into girl values
(1, '西施', 20),
(5, '王昭君', 23),
(8, '貂蝉', 25),
(10, '杨玉环', 26),
(12, '陈圆圆', 20);
レコードロック
単一のレコードをロックする
たとえば、id値が8のデータにレコードロックを追加すると、概略図は次のようになります。
レコードロックもSロックとXロックに分かれており、互換性は前述と同じです。
SQL実行に追加されるロックの種類は、トランザクションの分離レベルや実行中に使用されるインデックス(クラスター化インデックス、非クラスター化インデックスなど)などの多くの条件によって制限されるため、分析されません。詳細ですが、いくつかの簡単な例を示します。
-- READ UNCOMMITTED/READ COMMITTED/REPEATABLE READ 利用主键进行等值查询
-- 对id=8的记录加S型Record Lock
select * from girl where id = 8 lock in share mode;
-- READ UNCOMMITTED/READ COMMITTED/REPEATABLE READ 利用主键进行等值查询
-- 对id=8的记录加X型Record Lock
select * from girl where id = 8 for update;
ギャップロック
レコードの前のギャップをロックし、レコードの挿入を許可しません
MySQLは、MVCCと繰り返し可能な読み取り分離レベルでのロックにより、ファントム読み取りの問題を解決できます。
現在の読み取り:ロックされた
スナップショット読み取り:MVCC
しかし、それをロックする方法は?これらのファントムレコードは、読み取り操作を初めて実行したときには存在しないため、レコードロックを追加する方法はありません。現時点では、ギャップロックを追加する、つまりギャップをロックすることで解決できます。
たとえば、トランザクションがid = 8のレコードにギャップロックを追加する場合、他のトランザクションがid = 8のレコード、つまりid値のレコードの前のギャップに新しいレコードを挿入することを許可されないことを意味します。間隔(5、8)では許可されていません。すぐに挿入できます。間隔(5、8)のID値を持つレコードは、ギャップロックのあるトランザクションがコミットされるまで送信できません
次のSQLロックプロセスを見てみましょう
-- REPEATABLE READ 利用主键进行等值查询
-- 但是主键值并不存在
-- 对id=8的聚集索引记录加Gap Lock
SELECT * FROM girl WHERE id = 7 LOCK IN SHARE MODE;
id = 7のレコードが存在しないため、ファントム読み取りの現象を防ぐために(同じトランザクションで同じステートメントを実行して取得した結果セットでid = 7のレコードを回避するため)、他のトランザクションを防ぐ必要があります。現在のトランザクションがコミットされる前にIDを挿入することから。=7レコード、現時点では、id = 8のレコードにギャップロックを追加できます。つまり、他のトランザクションはid値の新しいレコードを挿入できません。間隔(5、8)で。
質問をさせてください。ギャップロックはレコードの前のギャップしかロックできないので、最後のレコードの後ろのギャップをロックするにはどうすればよいですか?
実際、mysqlデータはページに保存され、各ページには2つの疑似レコードがあります
- ページ内の最小レコードを表す最小レコード
- ページ内の最大のレコードを表すupremumレコード
他のトランザクションがid値が(12、+∞)の範囲にあるレコードを挿入するのを防ぐために、id = 12レコードが配置されているページのSupremumレコードにギャップロックを追加できます。このとき、他のトランザクションが(12)にid値を挿入するのを防ぐことができます、+∞)この間隔の新しいレコード
ネクストキーロック
同時に、データとデータの前のギャップをロックします。つまり、データとデータの前のギャップはレコード
の挿入を許可されないため、Next-key Lock = Record Lock+Gapを理解できます。ロック
-- REPEATABLE READ 利用主键进行范围查询
-- 对id=8的聚集索引记录加S型Record Lock
-- 对id>8的所有聚集索引记录加S型Next-key Lock(包括Supremum伪记录)
SELECT * FROM girl WHERE id >= 8 LOCK IN SHARE MODE;
ファントム読み取りの問題を解決するには、他のトランザクションがid>=8のレコードを挿入することを禁止する必要があります。
- id=8のクラスター化インデックスレコードにSタイプのレコードロックを追加します
- ID> 8のすべてのクラスター化インデックスレコード(Supremum疑似レコードを含む)にSタイプの次のキーロックを追加します
あなたにもっと良い理解を与えるために
テーブルレベルのロック
テーブルロックもSロックとXロックに分けられます
テーブルに対してselect、insert、update、およびdeleteステートメントを実行する場合、innodbストレージエンジンはテーブルレベルのSロックまたはXロックをテーブルに追加しません。
ALTERTABLEやDROPTABLEなどの一部のDDLステートメントがテーブルで実行されると、Xロックがテーブルに追加されるため、このテーブルでSELECT INSERTUPDATEDELETEなどのステートメントを実行すると他のトランザクションがブロックされます。
システム変数autocommit=0およびinnodb_table_locks=1の場合、InnoDBストレージエンジンによって提供されるテーブルtのSロックまたはXロックを手動で取得するには、次のように記述できます。
テーブルレベルのSロックをテーブルtに追加します
lock tables t read
テーブルレベルのXロックをテーブルtに追加します
lock tables t write
トランザクションがSロックをテーブルに追加する場合、
- 他のトランザクションは、テーブルのSロックを取得し続けることができます
- 他のトランザクションは、テーブル内の特定のレコードでSロックを取得し続けることができます
- 他のトランザクションは、テーブルのXロックを取得し続けることができません
- 他のトランザクションは、テーブル内の一部のレコードでXロックを取得し続けることができません
トランザクションがテーブルにXロックを追加する場合、
- 他のトランザクションは、テーブルのSロックを取得し続けることができません
- 他のトランザクションは、テーブル内の一部のレコードのSロックを取得し続けることができません
- 他のトランザクションは、テーブルのXロックを取得し続けることができません
- 他のトランザクションは、テーブル内の一部のレコードでXロックを取得し続けることができません
したがって、オンラインテーブルを変更する場合は、多数のトランザクションがブロックされるため、注意が必要です。現在、オンラインテーブルを変更するための成熟した方法は多数あるため、繰り返しません。
テーブルレベルには、ISロック(意図された共有ロック)とIXロック(意図された排他ロック)もあります。
モノがレコードにSロックをかけると、レコードに対応するテーブルにもISロックがかかります(ISロックはテーブルレベルのロックです)。モノがレコードにXロックをかけると、レコードに対応するテーブルのIXロック(IXロック)。ロックはテーブルレベルのロックです)
トランザクション分離レベルの実装
RR | RC | |
---|---|---|
通常選択 | MVCC | MVCC |
ロックされた選択と更新 選択…シャープモード 選択…更新 挿入 削除 更新 |
レコードロック ギャップロック ネクストキーロック |
レコードロック * |
リファレンスブログ
[1] https://souche.yuque.com/bggh1p/8961260/gyzlaf
[2]https://zhuanlan.zhihu.com/p/35477890
さまざまなロックの分類
[3]https://www.hacker66. me /detail/ZYXWPXH.html