MySQL はファントム リードの問題をどのように解決しますか?この問題については次の記事で解説していますので、疑問を持ちながら記事を読んでみましょう!
## ジン・ブサンとイン・ブシの頻繁なインタビュー質問の中で、MySQL のトランザクション特性、分離レベル、その他の問題も、非常に古典的な 8 部構成のエッセイの 1 つです。この種の質問に直面すると、ほとんどの友人はこれに精通していると推定されます:
トランザクション特性 (ACID): Atomicity (
Atomicity##) #)、分離
(分離
)、一貫性
(一貫性
)および永続性
分離レベル
: READ UNCOMMITTED(READ UNCOMMITTED
), READ COMMITTED
(READ COMMITTED
), Repeatable Read
(REPEATABLE READ
)、 Serializable
(SERIALIZABLE
)
そして、それぞれの種類の分離 レベルによって引き起こされる問題は次のとおりです。 :
READ UNCOMMITTED- 分離レベルでは、
ダーティ読み取り
、非反復読み取り
および##が発生する可能性があります。 #phantom readproblem
READ COMMITTED 分離レベルでは、- non-repeatable read
および
phantom read の問題が発生する可能性がありますが、これらは問題です。不可能です
ダーティ リード問題
REPEATABLE READ 分離レベルでは、- ファントム リード
問題が発生する可能性がありますが、
ダーティ リード#は発生しません## および Non-repeatable read
問題
SERIALIZABLE
分離レベルでは、さまざまな問題は発生しません
-
MySQL InnoDB の場合ストレージ エンジンでサポートされるデフォルトの分離レベルは
REPEATABLE-READ (反復可能) です。上記の SQL 標準の 4 つの分離レベルの定義から、
REPEATABLE-READ (反復可能)
となります。 ファントム リード を防ぐことは不可能ですが、MySQL InnoDB ストレージ エンジンがファントム リードの問題を解決することは誰もが知っています。では、どのようにしてそれを解決するのでしょうか?
1. 行フォーマット
本題に入る前に、まず行フォーマットとは何かを一般的に理解すると、次の MVCC を理解するのに役立ちます。行フォーマットは、テーブル内の行レコードがディスクに保存される方法です。Innodb ストレージ エンジンには、合計 4 つの異なるタイプの行フォーマットがあります:
compact
、redundant
, dynamic
, compress
; 行フォーマットには多くの種類がありますが、原理的には基本的に同じで、次のように、 compact
行フォーマット: As図からわかるように、完全なレコードは実際には 2 つの部分に分けることができます:
記録された追加情報 と
記録された実際のデータ . 記録された追加情報
はそれぞれ changes. 長いフィールド長リスト
、NULL 値リスト
および レコード ヘッダー情報
、および 記録された実際のデータ
独自に定義した列に加えて、 MySQL は、各レコードにいくつかのデフォルトの列を追加します。これらのデフォルトの列は、隠し列
とも呼ばれます。特定の列は次のとおりです:
列名
Length
Description |
|
|
row_id
6 バイト
一意の行 ID 1 つの Record |
|
transaction_id |
6 バイトを識別します
トランザクション ID |
|
roll_pointer |
7 バイト
ロールバック ポインタ |
|
非表示列の値について心配する必要はありません。 InnoDB ストレージ エンジンがそれを生成します。さらに詳細に描画しましょう。 compact 行形式は次のとおりです:
- transaction_id: トランザクション ID トランザクションが行レコードを変更すると、このトランザクションのトランザクション ID がこの列に割り当てられます。
##roll_pointer: 行が変更されるたびにレコードが変更されると、古いバージョンのデータが undolog ログに書き込まれます。- 続いて、roll_pointer
が undolog を指します。 なので、この列はポインターに相当し、これを介して「前の情報の変更」
#2. MVCC の詳細な説明
# を見つけることができます。 #2.1 バージョン チェーン
次のようなレコードがあるとします。 レコードに挿入された トランザクション ID は 80 であり、roll_pointer ポインタは NULL です (理解を容易にするため、読者はポインタが NULL であることを理解できます。実際は roll_pointer 最初のビットは、それが指す Undo ログのタイプをマークします。このビットの値が 1 の場合、それは意味します。これが指す undo ログ タイプが insert undo であることを示します) 次の 2 つを仮定します トランザクション ID100 と 200 のトランザクションがそれぞれ実行されますこのレコードに対する UPDATE 操作: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;"> -- 事务id=100
update person set grade =20 where id =1;
update person set grade =40 where id =1;
-- 事务id=200
update person set grade =70 where id =1;</pre><div class="contentsignin">ログイン後にコピー</div></div> レコードが変更されるたびに、 元に戻すログが記録されます。各 元に戻すログ roll_pointer 属性 (操作に対応する INSERT ) もあり、レコードには以前のバージョンがないため、##undo log にはこの属性がありません)、次のようにすることができます。これらの undo log をリンク リストに接続すると、現在の状況は下の図のようになります。 同じ: レコードが更新されるたびに、古い値が配置されます undo ログ、古いバージョンのレコードであっても、更新の数が増えると、すべてのバージョンが roll_pointer 属性によってリンク リストに接続されます。このリンク リストを バージョン チェーン と呼びます。バージョン チェーンのヘッド ノードは、現在のレコードの最新の値です。さらに、各バージョンには、対応する transaction id
#2.2 ReadView
# データベースの 4 つの分離レベル: 1) read uncommitted; 2) read committed; 3) REPEATABLE READ; 4) SERIALIZABLE; たとえば、 READ UNCOMMITTED 、毎回バージョン チェーンの最新データを読み取るだけです; SERIALIZABLE、主にロックによって制御されます; read commit と REPEATABLE READ これらはすべて、これら 2 つの分離レベルでは、バージョン チェーン内のどのものが現在のものに表示されるかが中心的な問題になります。この問題を解決するために、MySQL は読み取りビューの概念を提案しました。これには 4 つの中核的な概念が含まれています。 m_ids : read view- を生成するとき、アクティブなモノ ID コレクション
min_trx_id :# # の最小値#m_ids 、つまり、読み取りビューを生成するときのアクティブなものの最小値 -
max_trx_id: 読み取りビュー を生成するときに、システムが次のモノの ID 値 -
creator_trx_id を割り当てる必要があります。これは、 read view を作成するモノの ID (現在のモノの ID) です。 -
このReadView を使用すると、レコードにアクセスするときに、次の手順に従うだけで、レコードの特定のバージョンが表示されているかどうかを確認できます。
記録されたモノの ID が creator_trx_id と等しい場合、現在のモノが変更したレコードにアクセスしていることを意味するため、このバージョンは表示されます アクセスされたバージョンの場合Thing ID が min_trx_id より小さい場合、- read view
が作成されたときに Thing が送信され、このバージョンが現在の Thing に読み取り可能であることを意味します
Ifアクセスされたモノのバージョン ID が - max_trx_id
以上の場合、 read view が作成されるときに、バージョン レコードを生成するモノ ID が開かれていないことを意味します Read view が生成されるまで、このバージョンは現在の Thing -
で読み取ることができません。アクセスされたバージョンの Thing transaction_id が m_ids# にある場合、 ## コレクションの場合、Read view が生成された時刻、トランザクションがまだアクティブで送信されていない場合、バージョンにアクセスできないことを意味します。そうでない場合は、バージョンを生成したトランザクションを意味します。 ReadView- は作成され、送信され、アクセスできます
注: 読み取り用の ID は 0 ですMySQL では、 READ COMMITTED と REPEATABLE READ 分離レベルの大きな違いは、異なるタイミングで ReadView を生成することです。
- READ COMMITTED
—— ReadView
- REPEATABLE READ
を生成します —— 毎回データを読み取る前に、データを 1 回読み取るときに、 ReadView
が生成されます。詳細な例を使用して 2 つの違いを説明しましょう:
Timenumber |
| trx 100 | trx 200 |
① | BEGIN; |
|
|
#②
|
BEGIN; |
BEGIN; |
|
③
|
個人セットのグレード =20 (ID = 1) を更新します; |
|
|
④
|
##個人セットのグレード =40 (ID =1) を更新します;
|
|
⑤ |
SELECT * FROM person WHERE id = 1;
|
|
|
⑥ |
##コミット; |
|
|
⑦
|
| ##個人セットのグレード =70 (ID =1) を更新します。
|
⑧
| SELECT * FROM person WHERE id = 1; |
|
|
9 |
|
| コミット;
|
?
| コミット; |
|
|
##時刻④では、トランザクション | trx 100 によりトランザクションが送信され、id=1 行に記録されているバージョン チェーンは次のようになります。
|
時間⑥では、トランザクション trx 200 により、トランザクションのコミットが実行され、バージョン チェーンが id=1 行に記録されます。 次のように:
時間 ⑤ では、トランザクション trx 100 は、 select ステートメントの実行時に最初に ReadView## を生成します。 ReadView の m_ids リストの [100, 200]、 min_trx_id は 100、 max_trx_id は 201、 creator_trx_id は 0 です。この時点では、バージョン チェーンから表示されているレコードを選択し、バージョン チェーンを上から下までたどります。下: Grade=40 であるため、 trx_id の値は m_ids にある 100 であるため、レコードは表示されません。 =20も見えません。引き続き下に移動し、グレード = 20、 trx_id 値は 80 であり、 ReadView の min_trx_id 値 100# よりも小さいです。 ## したがって、このバージョンは要件を満たしており、レベル 10 のレコードがユーザーに返されます。 時間 ⑧ で、トランザクションの分離レベルが READ COMMITTED の場合、別の ReadView (ReadView# の ##) が生成されます。 m_ids リストの内容は [200] 、min_trx_id は 200、 max_trx_id は 201 , creator_trx_id は 0 です。このとき、バージョン チェーンから表示されているレコードを選択すると、バージョン チェーンが上から下にたどられます: Grade=70 であるため、 trx_id の値は m_ids の 200 であるため、レコードは表示されません。トラバースを続けます。grade=40、 trx_id値は 100 です。これは ReadView の min_trx_id 値 200 より小さいため、このバージョンは要件を満たしており、レコードは次のようになります。レベル 40 がユーザーに返されます。 時間 ⑧ で、トランザクションの分離レベルが REPEATABLE READ である場合、時間 8 で、 ReadView は個別に生成されず、時間ごとに生成されます。 ReadView では 5 が使用されるため、ユーザーに返されるレベルは 10 です。 2 つの選択の結果は同じです。これが 繰り返し読み取り の意味です。
3. 概要
MVCC の詳細な説明を分析すると、MVCC に基づいて、RR 分離レベルの下で、非常に簡単に解決できると結論付けることができます。 ファントム読み取り問題ですが、 select for update が現在の読み取りを生成し、スナップショット読み取りではなくなっていることがわかっています。この場合、MySQL は ファントム読み取り をどのように解決しますか?問題?時間の問題 (整理して図を描くにはかなりの時間がかかります) に基づいて、最初に結論を述べてから、現在の読み取り状況で MySQL が ファントム読み取り 問題をどのように解決するかを分析します。
現在の読み取り値 : Next-Key Lock (ギャップ ロック) を使用して、ファントム読み取りが発生しないようにロックします。
ギャップ ロックの使用方法現在の読書状況 ファントム リーディングの問題を解決したい場合は、興味のある友人がフォローといいねを追加してください [関連する推奨事項: mysql ビデオ チュートリアル ]
|
以上がMySQL がファントム読み取り問題をどのように解決するかを簡単に分析した記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。