ホームページ  >  記事  >  データベース  >  mysql の RR とファントム読み取りに関連する問題

mysql の RR とファントム読み取りに関連する問題

WBOY
WBOY転載
2022-10-11 16:59:021939ブラウズ

この記事では、mysql に関する関連知識を提供します。主に、MVCC 原理、ファントム読み取りを生成する RR、ファントム読み取りを解決する RR など、RR とファントム読み取りに関する関連コンテンツを紹介します。以下の内容が皆様のお役に立てれば幸いです。

mysql の RR とファントム読み取りに関連する問題

推奨学習: mysql ビデオ チュートリアル

1. はじめに

この記事では、これら 3 つのトピックに焦点を当てます。 RR ファントム・リーディングを解決するにはどうすればよいですか?

mysql の RR とファントム読み取りに関連する問題

  • MVCC 原則

  • 実験: RR とファントム読み取り

  • ケース: デッドロック

  • ## まず、4 種類のトランザクション分離と、MySQL の InnoDB でサポートされている同時トランザクションによって引き起こされるいくつかの問題を確認してみましょう。

mysql の RR とファントム読み取りに関連する問題

    Read uncommitted: トランザクションの中間処理を読み込むことができるため、ACID の特性に違反し、ダーティ リードの問題があるため、基本的には使用されません。
  • コミットの読み取り: 他のトランザクションがコミットされているかどうかを確認できることを示します。実稼働環境ではあまり使用されません。
  • 反復読み取り: デフォルトのレベルで、最もよく使用されるレベルです。ギャップロック機能を搭載しています。
  • シリアル化可能: すべての実装はロックを通じて実装されます。
  • 同時トランザクション処理は、ダーティ リード、反復不可能な読み取り、ファントム リードなどの問題も引き起こします。

    ダーティ リード: トランザクションは処理を行っています。レコードが変更されると、トランザクションが完了して送信されるまで、このレコードのデータは不整合な状態になります。
  • Non-repeatable read: 同じクエリ条件に従ってトランザクションが 2 回読み取られ、読み取られたデータが不整合 (変更、削除) されます。
  • ファントム リーディング: トランザクション内で同じクエリ条件に従ってデータを再クエリしたが、他のトランザクションがそのクエリ条件を満たす新しいデータを挿入したことがわかります。
  • この記事の概要: RR は同時実行を高速化するために MVCC を導入していますが、ファントム読み取りの可能性があります。ファントム読み取りを解決するために、ギャップ ロックが導入されています。ギャップによりデッドロックが発生する可能性があります。

2. MVCC 原則

MVCC (Multiple Version Control): 高度な同時データ アクセス、データのマルチバージョン処理を実現し、トランザクションの可視性を通じてトランザクションの可視性を確保するためのデータベースを指します。表示すべきデータのバージョンを確認できます。

MVCC の最大の利点は、読み取りにロックがなく、読み取りと書き込みの間に競合がないことです。

OLTP (オンライン トランザクション処理) アプリケーションでは、読み取りと書き込みの間に競合がないことが重要であり、ほとんどすべての RDBMS が MVCC をサポートしています。

注: MVCC は、読み取りコミット RC と反復読み取り RR の 2 つの分離レベルでのみ機能します。

注: MVCC は、読み取りコミット RC と反復読み取り RR の 2 つの分離レベルでのみ機能します。

注: MVCC は、読み取りコミット RC と反復読み取り RR の 2 つの分離レベルでのみ機能します。

(1) MVCC マルチバージョンの実装MySQL が MVCC メカニズムを実装する場合、それは Undo ログのマルチバージョン チェーンに基づいています。 ReadView メカニズム。

    元に戻すログのマルチバージョン チェーン: データベースが変更されるたびに、現在の変更レコードのトランザクション番号と変更前のデータ状態の格納アドレス (つまり ROLL_PTR) が変更されます。元に戻すログに記録されるため、必要に応じて古いデータ バージョンにロールバックできます。
  • ReadView メカニズム: マルチバージョン チェーンに基づいて、トランザクション読み取りの可視性を制御します。 (主な違いは: RC と RR)
  • ここでは原理の検討には重点を置きませんが、UNDO ログのマルチバージョン チェーンと ReadView という一般的な概念を理解する必要があります。機構。

UNDO ログのマルチバージョン チェーンの例を次に示します。

    読み取りトランザクションは現在のレコードをクエリしますが、最新のトランザクションはまだ送信されていません。
  • 原子性に従って、読み取りトランザクションは最新のデータを参照できませんが、ロールバック セグメント内の古いバージョンのデータを見つけることができるため、複数のバージョンが生成されます。
  • ReadView メカニズムの場合: Undo ログのマルチバージョン チェーンの実装に基づいて、トランザクション分離ごとに異なる処理が行われます:

    RC レベルのトランザクション: 可視性より高いレベルでは、コミットされたトランザクションのすべての変更を確認できます。
  • RR レベルのトランザクション: 読み取りトランザクションでは、他のトランザクションがデータにどのような変更を加えても、それらが送信されるかどうかに関係なく、クエリ データの結果は変更されない限り変更されません。提出しないでください。
  • これはどのように行われるのでしょうか?

RC 読み取り送信: 各読み取り操作ステートメントは ReadView を取得します。各更新後、データベース内の最新のトランザクション送信ステータスが取得され、最新に送信されたトランザクション、つまり各ステートメントを確認できます。実行すると可視性ビューが更新されます。

RR 反復読み取り: トランザクションの開始時に ReadView は取得されず、最初のスナップショット読み取りが開始されたときにのみ ReadView が取得されます。

現在の読み取りを使用すると、新しい ReadView が取得され、更新されたデータも表示されます。

#(2) スナップショット読み取りと現在の読み取り

MVCC 同時実行制御では、読み取り操作は 2 つのカテゴリに分類できます:

スナップショット読み取り: 読み取られるのは、ロックされていない、レコードの表示されているバージョン (おそらく履歴バージョン) です。

操作: 単純な SELECT 操作。

現在の読み取り: レコードの最新バージョンが読み取られ、現在の読み取りによって返されたレコードは、他のトランザクションがこのレコードを同時に変更しないようにロックされます。

操作: 特殊な読み取り操作、追加/更新/削除操作。

-- 对应 SQL 如下:
-- 1. 特殊读操作
SELECT ... FOR UPDATE
SELECT ... LOCK IN SHARE MODE  -- 共享锁
-- 2. 新增:INSERT 
-- 3. 更新:UPDATE
-- 4. 删除:DELETE

ReadView メカニズムと組み合わせて、スナップショット読み取りと現在の読み取りを区別します。

スナップショット読み取り: トランザクションでは、最初のスナップショット読み取りが開始されたときにのみ ReadView が取得され、その後の読み取りが開始されます。操作は再度取得されません。

現在の読み取り値: ReadView は読み取り操作ごとに取得されます。

3. 実験: RR とファントム読み取り

インタビューの質問: RR トランザクション分離レベルでは、トランザクション A はデータの一部をクエリし、トランザクション B はデータの一部を追加し、トランザクション A はトランザクション B を参照してください。 データは?

mysql の RR とファントム読み取りに関連する問題

この質問は比較的曖昧ですが、一般的な検査ポイントは RR とファントム リーディングであることがわかっています。質問は 2 つのカテゴリに分類できます:

RR はどのような状況でファントム リーディングを生成しますか? (データを見ることができます)

答え: 現在の読み取り (SELECT..FOR UDPDATE、SELECT ... LOCK IN SHARE MODE)

RR はどのような状況でファントム読み取りを解決しますか? (データは表示されません)

回答: ロック、スナップショット読み取り

注: 反復不可能な読み取りは UPDATA と DELETE に重点を置き、ファントム読み取りは INSERT に重点を置きます。

両者の最大の違いは、ロック機構を通じて引き起こされる問題をどのように解決するかです。

ここで説明するロックは、悲観的ロック機構のみを使用します。

もう一度確認してみましょう: ファントム読み取り

-- 举个栗子:有这样一个查询 SQL
SELECT * FROM user WHERE id < 10;

同じトランザクションで、T1 時点で 4 個のデータがクエリされ、8 個のデータがクエリされますT2時。これによりファントムリーディングが発生します。

同じトランザクションで、時刻 T1 に 8 個のデータがクエリされ、時刻 T2 に 4 個のデータがクエリされます。これによりファントムリーディングが発生します。

実験の準備は次のとおりです。 実践的な演習

show variables like &#39;transaction_isolation&#39;; -- 事务隔离级别 RR
select version();                            -- 版本 8.0.16
show variables like &#39;%storage_engine%&#39;;      -- 引擎 InnoDB
-- 1. 手动开启事务提交
begin;  -- 开始事务
commit; -- 提交事务
-- 2. 创建表
CREATE TABLE IF NOT EXISTS `student` (
`id` INT NOT NULL COMMENT &#39;主键 id&#39;,
`name` VARCHAR(50) NOT NULL COMMENT &#39;名字&#39;,
`age` TINYINT NOT NULL COMMENT &#39;年龄&#39;,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT &#39;学生表&#39;;
-- 3. 新增数据用于实验
INSERT INTO student (id, name, age) VALUES (5, &#39;kunkun&#39;, 14);
INSERT INTO student (id, name, age) VALUES (30, &#39;ikun&#39;, 18);

(1) RR はファントム リーディングを生成します

実験は次のとおりです: 現在の読み取りをテストします

実験 1: 最初に SELECT、次に SELECT... FOR UPDATE

実験 2: 最初に SELECT、次に UPDATE (ファントム読み取りは発生しません)

実験 1: 最初に SELECT、次に SELECT... FOR UPDATE

-- 事务A:
BEGIN;
SELECT * FROM student WHERE id < 30;
SELECT * FROM student WHERE id < 30 FOR UPDATE;  -- 等待事务B commit 后再执行
-- SELECT * FROM student WHERE id < 30 LOCK IN SHARE MODE;
COMMIT;
-- 事务B:
BEGIN;
INSERT INTO student (id, name, age) VALUES (20, &#39;wulikun&#39;, 16);
COMMIT;

何が起こったかは、次の図に示すとおりです。

mysql の RR とファントム読み取りに関連する問題

実験記録は以下の図のとおりです。

mysql の RR とファントム読み取りに関連する問題

現象の結論: 現在の読み取り (SELECT ... FOR UPDATE) を使用すると、ファントム読み取りが発生します。

同様に、SELECT ... LOCK IN SHARE MODE; を使用すると、ファントム読み取りが発生します。

mysql の RR とファントム読み取りに関連する問題

#実験 2: 最初に SELECT、次に UPDATE

-- 事务A:
BEGIN;
SELECT * FROM student WHERE id < 30;
UPDATE student SET name = &#39;zhiyin&#39; WHERE id = 5;  -- 等待事务B commit 后再执行
SELECT * FROM student WHERE id < 30;
COMMIT;
-- 事务B:
BEGIN;
INSERT INTO student (id, name, age) VALUES (20, &#39;wulikun&#39;, 16);
COMMIT;
何が起こるかは以下のようになります:

mysql の RR とファントム読み取りに関連する問題実験記録を次の図に示します。

mysql の RR とファントム読み取りに関連する問題 現象の結論: 現在の読み取り (UPDATE) は行われません。ファントムリードを生成します。 INSERT / DELETE のどちらでも同じことは行われません。

mysql の RR とファントム読み取りに関連する問題

(2) RR はファントム読み取りを解決します 実験は次のとおりです。

    実験 1: スナップショットの読み取り
  • 実験 2: ロック (存在しないレコードの更新)
  • 実験 3 : ロックの追加 (SELECT ... FOR UPDATE)
実験 1: スナップショット読み取り、通常の SELECT

-- 事务A:
BEGIN;
SELECT * FROM student;
SELECT * FROM student;  -- 等待事务B commit 后再执行
COMMIT;
-- 事务B:
BEGIN;
INSERT INTO student (id, name, age) VALUES (20, &#39;wulikun&#39;, 16);
COMMIT;
何が起こるかは次のようになります:

mysql の RR とファントム読み取りに関連する問題実験記録を次の図に示します。

mysql の RR とファントム読み取りに関連する問題現象の結論: RR トランザクションの下で分離レベルでは、スナップショット読み取り (SELECT) のみがファントム読み取りを引き起こしません。現在の読み取りはありません。

実験 2: ロック (存在しないレコードの更新)

RR 分離レベルでは、トランザクション A は UPDATE を使用してロックし、トランザクション B は新しいデータを挿入できません。これにより、UPDATE の前後でトランザクション A によって読み取られたデータの一貫性が保たれ、ファントム読み取りが回避されます。

-- 事务A:
BEGIN;
SELECT * FROM student;
UPDATE student SET name = &#39;wulikunkun&#39; WHERE id = 18; -- 记录不存在,产生间隙锁 (5, 30)。
COMMIT;
-- 事务B:
BEGIN;
INSERT INTO student (id, name, age) VALUES (10, &#39;zhiyin&#39;, 16); -- 需要等待事务A结束。
COMMIT;
-- 事务C:
BEGIN;
INSERT INTO student (id, name, age) VALUES (40, &#39;zhiyin你太美&#39;, 32);
COMMIT;
-- 查询数据库中当前有哪些锁
SELECT INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA FROM performance_schema.data_locks;

何が起こったかは次のとおりです:

mysql の RR とファントム読み取りに関連する問題

实验记录如下图所示:

mysql の RR とファントム読み取りに関連する問題

现象结论:

一开始先加 临键锁Next-key lock,锁范围为 (5,30]。

因为是唯一索引,且更新的记录不存在,临键锁退化成 间隙锁Gap,最终锁范围为 (5,30)。其余的记录不受影响。

实验三:加锁(SELECT ... FOR UPDATE)

-- 事务A:
BEGIN;
SELECT * FROM student;
SELECT * FROM student WHERE id < 5 FOR UPDATE;
COMMIT;
-- 事务B:
BEGIN;
INSERT INTO student (id, name, age) VALUES (4, &#39;zhiyin&#39;, 4); -- 需要等待事务A结束。
COMMIT;
-- 事务C:
BEGIN;
INSERT INTO student (id, name, age) VALUES (5, &#39;zhiyin你太美&#39;, 32); -- 插入成功
COMMIT;
-- 查询数据库中当前有哪些锁
SELECT INDEX_NAME,LOCK_TYPE,LOCK_MODE,LOCK_STATUS,LOCK_DATA FROM performance_schema.data_locks;

发生情况如下图所示:

mysql の RR とファントム読み取りに関連する問題

实验记录如下图所示:

mysql の RR とファントム読み取りに関連する問題

现象结论:

先加 临键锁Next-key lock,锁范围为 (-∞,5]。

所以,id

拓展:Gap 锁(间隙锁)

根据 官方文档 可知:

  • 锁是加在索引上的。

  • 记录锁: 行锁,只会锁定一条记录。

  • 间隙锁 :是在索引记录之间的间隙上的锁,区间为前开后开 (,)。

  • 临键锁(Next-Key Lock): 由 记录锁 和 间隙锁Gap 组合起来。

  • 加锁的基本单位是 临键锁,其加锁区间为前开后闭 (,]。

  • 索引上的等值查询,给唯一索引加锁的时候,如果满足条件,临键锁 退化为 行锁。

  • 索引上的等值查询,给唯一索引加锁的时候,如果不满足条件,临键锁 退化为 间隙锁。注意,非等值查询是不会优化的。

推荐学习:mysql视频教程

以上がmysql の RR とファントム読み取りに関連する問題の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.imで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。