InnoDB トランザクション原則の理解 (REDO ログ、UNDO ログ、ロック、MVCC の理解)

事務

トランザクションは MySQL の InnoDB エンジンに固有であり、一連の SQL ステートメントは分割できない全体とみなされ、全体内のすべての SQL ステートメントの実行は成功または失敗します。

ACIDトランザクションの4つの主要な特徴

  • 原子性 Atomicity: 原子は分割できない小さな単位です。トランザクション内のすべての SQL は、すべてが成功するかすべてが失敗するかのいずれかである全体とみなされ、分割できない最小単位となります。
  • 一致性 Consistency: トランザクションの実行前後のデータは一貫した状態に保たれ、データの総量は変化しません。例:Aが10元、Bが10元を持っているとき、AはBに10元を送金したいと考えています。この時点で取引が開始され、Aは10元を減算し、Bは10元を追加して取引が送信されます。提出後、Aさんの所持金は0元、Bさんの所持金は20元となり、合計金額は変わらず、消えることはありません。
    では、取引が始まったら、Aさんの口座から直接10元を引くのは矛盾ではないでしょうか?私の理解:Aはトランザクションを使用せずに直接SQLを操作します
  • 隔离性 Isolation: トランザクションは同時スレッドの影響を受けず、各スレッドは独立した環境で実行され続けます。例: A がトランザクションを開始し、ID 2 のデータを 0 に変更します。トランザクションが送信される前は、他のスレッドは ID 2 のデータを操作できないため、複数のスレッド下でも独立した環境で実行できます。
  • 持久性 Durability: トランザクションがコミットまたはロールバックされると、それは永続的で変更できません。例: A がトランザクションを開始し、10 元を B に送金します。トランザクションが送信されると、この操作の 10 元は決して戻ってきません。トランザクションがロールバックされると、この操作の 10 元は再び B に送金されません。 B.

トランザクション関連のSQL文

トランザクションを開いたらロールバックまたはコミットが必要ですが、手動操作がなくてもタイムアウト後に自動的にロールバックされます。「」を開いて送信し、ロールバックするのは一連の操作です。

  • トランザクションがオープンしているbegin;か、START TRANSACTION;
  • トランザクションのコミットcommitl
  • トランザクションのロールバックrollback;

トランザクションはデフォルトで自動的に送信されます。つまり、各 SQL は独立したトランザクションです。トランザクションに一連の SQL を含める場合は、手動トランザクション送信を有効にする必要があります。

  • 現在のセッションのトランザクションが自動的にコミットされているかどうかを確認します。SELECT @@autocommit 結果は 1 が自動的にコミットされ、0 が手動でコミットされます。
  • 現在のトランザクションを手動コミットに設定するSET @@autocommit = 0 ;

経営理念

トランザクションが孤立を解決する方法

トランザクションに ACD のみがあり、分離 I がない場合、スレッドの安全性の問題に直面します。
データベース サーバーは複数の接続で同時に使用でき、接続ごとにサーバー内にそのスレッドが作成され、複数のスレッドが連携して動作します。次に、複数のスレッド間でデータをどのように操作するかという問題があります。例: id=1 の金額は最初は 0 元で、スレッド A がトランザクションを開始し、id=1 の金額に 10 元を追加します。このとき、スレッド B がトランザクションを開始し、id=1 の金額から 20 元を減算します。 =1、A がトランザクションを送信し、B がトランザクションをロールバックします。理想的には id=1 にすべき金額は 10 元ですが、実際には -10 元になります。これは、複数のスレッド間で同じデータを操作するために発生します线程安全问题この例は、 の多くのセキュリティ問題の中で最も深刻なものです脏写
マルチスレッドによって引き起こされる問題:重大度は低から高まであります。スレッド、 の
間で同じデータに対して発生する可能性があります読み取りと読み取りにはセキュリティ上の問題はありませんが、書き込みと読み取りと書き込みは、対応するダーティ ライトとダーティ リードに分割されます。写写读写读读

  • 脏写: 上記の例は、マルチスレッド操作を実行したい限り、ダーティ ライトの問題を解決する必要があります。
  • 脏读: スレッドが別のスレッドからコミットされていないデータを読み取ります。たとえば、id=1 の金額が 0 元で、ユーザー A がトランザクションを開始して 10 元を送金すると、スレッド B は id=1 と 10 元をクエリして対応する操作を実行し、Aロールバックしてid=1のスレッドが0元に戻るのですが、スレッドBの動作が間違っているのでしょうか?

事务使用锁来解锁多线程安全问题(詳細は後ほど紹介します)

  • 脏写解决方法:: スレッドは書き込み時にスレッド ロックを追加します。他のスレッドはデータを操作する前にロックがあるかどうかを確認します。ロックがない場合は操作します。ロックがある場合、どの操作も A スレッドがロックを解放するまで待機しますこのように、スレッド Aが動作しているときは、他のスレッドは参加できず、処理を進めることしかできず读取、スレッド A の独立した動作空間が形成されますが、ダーティ リードの問題があります。
  • 脏读解决方法:: 前回を踏まえて、他のスレッドが操作対象のデータにロックがあると判断した場合、any は待機しますこのようにして、このデータに対するスレッド A の操作は、トランザクションがコミットされる前に他のスレッドの書き込みによって妨げられることはなく、他のスレッドからは認識されません。
    読んでどうやって判断するの?スレッドの読み取りと書き込みを区別するにはどうすればよいですか? したがって、ロックは読み取り/書き込みロックに分割され、書き込みロックは排他ロック、読み取りロックは共有ロックになります。在写时上写锁,使得其他线程上读锁和写锁都失败,在读时上读锁,使得其他线程上读锁成功,上写锁失败つまり、写时不能读,读时不能写,写时不能写,读时可以读。
    ロックのみを使用することによって引き起こされる問題ですか? 1 つのスレッドが書き込みを行う場合、他のスレッドは読み取りも書き込みもできず、前のロックが解放されるのを待つことしかできず、シングルスレッド操作になります。影响线程的并发性

多版本并发控制MVCC的使用: (詳しくは後述します) MVCC はスレッドの読み取り効率のみを向上させることができます。
すべてのデータには、変更後の対応する履歴バージョンがあります。他のスレッドが書き込みを行うと、現在書き込みできないデータは読み取れないため、現在のトランザクションがコミットされない前のデータの履歴バージョンを読み取ることができます。これにより、読み取りを待機しているスレッドが減り、同時実行性が向上します。
しかし、これには次の 3 つの問題が生じます。

  • 不可重复读:データの最初の読み取りとデータの 2 回目の読み取りの間に、スレッドがデータを変更するトランザクションをコミットすると、読み取られたデータのバージョンが不一致になり、その前後でデータが不一致になります。AとBは同時に1,000元の口座を運営しています。A が取引を開始して口座残高が 1,000 元であることを確認すると、B は取引を開始して口座に 1,000 元をチャージしたことになります。このとき、A が再度口座残高を確認すると、残高は 1,000 元のままです。B が取引を送信した後、 , Aさんが3度目に口座を確認すると、残高が2000に変わっていることがわかりました。同じトランザクションで同じデータがクエリされ、結果は一貫性がありません。解决此问题很简,一个事务中只读取一个版本数据即可, しかし、次の問題が発生します
  • 幻读(只有插入数据时遇到): 読み込まれていませんが、データは本物です。読み取りは履歴バージョンである可能性があるため、非反復読み取りを解決するために、現在データを操作しているスレッドがトランザクションをコミットしても、データを読み取ることはできません。ただし、ロックの取得に成功した場合、データを挿入するということは最新のデータを挿入することになるため、最新のデータは過去のバージョンと一致しない可能性があり、読むのが現実的ではなく、まるで幻覚を見ているかのように感じられます。例: スレッド A がトランザクションを開始し、ID=1 のユーザー名を変更します。これは、ID が 1 のユーザーです。スレッド B は、トランザクションが送信される前にデータ バージョンを読み取ります。A が変更を完了した後、次に、ID が 2 のデータが送信されます。スレッド B は、履歴バージョンを読み取ったため、ID 2 のデータをクエリしませんでしたが、挿入したときに、ID 2 のデータがすでに存在していることがわかりました。質問: B スレッドがクエリを実行する場合、どのレベルのロックが使用されますか?
    解决方法: ギャップロックまたは一時構築ロックを使用して、クエリ間隔でのデータ挿入を防止し、データを挿入できないため、ファントムリードの問題は解決されます。MySQL在可重复读阶段就通过锁的方式解决了幻读しかし、readview 中に読み取りがロックされている場合、メインスレッドがデータの追加を終了した場合はどうなるでしょうか? メインスレッドがロックされている場合、メインスレッドがビジネスを実行できるかどうかに問題が発生します。

分離の概要

分離が直面する問題: スレッドの同時実行性と MVCC によって引き起こされる問題

  • 脏写: 解決する必要があります

  • 脏读
    ここに画像の説明を挿入

  • 不可重复读
    ここに画像の説明を挿入

  • 幻读
    ここに画像の説明を挿入
    同時実行効率を向上させるために、InnoDB エンジンは分離においてトレードオフを行います。分離レベルが高くなるほど、システムの同時実行性は低くなります。トレードオフの度合いに応じて、セキュリティの低いものから高いものまで 4 つのタイプに分類されます。

  • 读未提交 RU: ダーティ ライトの問題を解決します。スレッド DML がデータを操作するとき、そのデータは変更できないようにロックされ、他のスレッド DML は操作できませんが、読み取ることはできます。たった今脏读
    ここに画像の説明を挿入

  • 读已提交 RC: ダーティ リードとアンコミット読み取りの問題を解決しました。スレッド DML がデータを操作するとき、データをロックして変更や現在のデータの読み取りができないようにします。読み取りできるのは MVCC の ReadView のみです。

  • 可重复读 (默认)RR: 異なるバージョンの読み取りによって発生するデータの不整合の問題を解決するには、ReadView の 1 つのバージョンのみを読み取ります。

  • 序列化: 書き込み操作中、他のスレッドによる読み取りは待機することしかできません。
    ここに画像の説明を挿入

トランザクションがアトミック性、一貫性、耐久性をどのように解決するか

データ書き込み操作がディスクを直接操作すると、ランダム IO が発生してパフォーマンスに大きな問題が発生する可能性があるため、最初にメモリから取得し、メモリ上のデータを操作し、最後に他のスレッドによってバッチでディスクに書き込みます。 。
永続性:
メモリ内のダーティ ページを最終的にディスクに確実に書き込むためには、起こり得る突発的な状況 (ダウンタイム、停電など) を防ぐ必要があり、記録には REDOLOG ログが必要です。redologログは最初にメモリに書き込まれ、トランザクションがコミットされた後にディスクに書き込まれます (頻度は設定可能)。シーケンシャル IO 速度はデータのランダム IO 速度よりも高速です。トランザクションをコミットした後にトランザクションがディスクにフラッシュされていない場合は、ダウンタイムが発生します。再起動後、データの永続性を確保するために、redolog の内容が再度読み書きされます。トランザクションが開始されてもコミットされていないときにダウンタイムが発生した場合、メモリ データと redolog メモリ レコードは消えますが、トランザクションがコミットできなかったことを示すプロンプトが表示され、ディスク データは変更されないため、永続性が確保されます。トランザクションが正常にコミットされると、redolog 内のこのトランザクションに関するレコードは役に立たなくなり、新しいレコードによって上書きされます。

アトミック性: トランザクションが正常にコミットされた場合、またはトランザクション中にメモリデータが消失した場合、上記の状況が発生します。コード レベルで事故が発生した場合、トランザクションはロールバックされるか、手動でロールバックされます。これは、トランザクションの開始前とトランザクションのロールバック時に、メモリ データに対するすべての操作をトレースバックする必要があることを意味します。redolog ログには、ディスク上のデータが変更された場所とその理由のみが記録されますが、メモリ内のデータが変更される前はどのようになっていたかが記録されます。これにはundologログが必要です。SQL メモリのデータが変更されるたびに、データの変更を復元するために逆の SQL がログに記録されます。トランザクションが正常に送信されると、トランザクションはディスクに保存されます。トランザクションをロールバックすると、前のデータ、つまり原子性に戻ることができます。
一貫性: アトミック トランザクションでは、すべての SQL が成功するか失敗します。永続性では、正常にコミットされたすべてのトランザクションがディスクにフラッシュされ、この 2 つの組み合わせによって一貫性が実現されます。

やり直しログ やり直しログ

redolog の役割は、メモリ内のコミットされたトランザクションで変更されたデータを脏页ディスクのログ バックアップに確実にフラッシュできるようにすることです。
redolog は 2 つの部分に分かれており、1 つの部分はredo log bufferデフォルト、もう 1 つの部分はディスク ファイルですredo logo file。ファイルは 2 つあり、それぞれ 48M あり ib_logfile0 ib_logfile1ループで書き込むことができます。

Buffer Poolデータを変更する場合は、ディスク IO の読み取りおよび書き込み速度が遅すぎるため、まずデータをメモリ、つまり変更用の InnoDB エンジンにロードする必要があります。ただし、トランザクションがコミットされた後、メモリ内のすべてのデータがディスクに更新され、軽微な変更ごとにランダム IO が必要になる可能性もあり、非効率的です。これを行わないと、最悪の場合、メモリの変更が完了した後、トランザクションが開始されると、トランザクションは正常に送信されますが、送信後にシステムが突然クラッシュし、その結果、メモリ内のダーティ ページがフラッシュされない可能性があります。ディスクが破損し、データ損失が発生し、永続性が侵害されます。
ここに画像の説明を挿入

InnoDB の解決策は、メモリ内のデータを変更するときに、同時にデータ変更を REDO ログ バッファに書き込むことであり、トランザクションがコミットされた後、メモリ内のダーティ ページはすぐには更新されず、メカニズムが引き継がれます。データのファイナリティを確保するためcheckpoint。REDO ログ ディスク ファイルの書き込み量は顺序写入大きく、コンテンツは小さいため、REDO ログ バッファ内のコンテンツはディスクに書き込まれます。これは、データを直接ディスクにフラッシュするよりもはるかに高速です。 。トランザクションは、REDO によるディスクへの書き込みが完了するまでコミットされません。この期間中に事故が発生した場合、トランザクションは直接価格の引き上げに失敗し、ロールバックされます。トランザクションがコミットされた後は、対応する REDO ログのバックアップが存在する必要があることを確認してください。
ディスクのリフレッシュ処理中にダーティ ページ データが誤って失われた場合は、サービスの再起動後にディスク ファイル REDO ログを通じて復元できます。REDO ログはディスク上のどの場所にどのようなデータがあるかを記録しており、永続的なファイルであるためです。このように、メモリデータの安全性の問題は、一部の REDO ログによって解決されます。このテクニックは、この画像に 2 つのブラシがあるWAL (Write-Ahead Log)場合にも呼び出されますredolog中记录对应的脏页已经成功刷新到了磁盘则此记录也没有用了,会被新记录覆盖
ここに画像の説明を挿入
ここに画像の説明を挿入

  1. redo は、トランザクションがコミットされた後に、REDO バッファー内のレコードをディスクにフラッシュします。
  2. メモリ内のダーティ ページは定期的にディスクにフラッシュされます

トランザクションの送信後に REDO ログをフラッシュする必要はありませんが、フラッシュの頻度は構成できます。
ここに画像の説明を挿入

  • トランザクションの送信後、ディスクはフラッシュされず、REDO ログ バッファの内容はマスター スレッドによって毎秒ディスクにフラッシュされます。これによりデータ損失が発生する可能性がありますが、IO の数は少なく、効率は高くなります。

  • トランザクションがコミットされるとディスクがフラッシュされ (デフォルト)、ディスクも 1 秒ごとにフラッシュされます。したがって、トランザクションのコミット時にREDOのディスクフラッシュを行う必要はありません。トランザクション処理中は1秒の間隔でディスクをフラッシュします。メモリ使用量が半分を超えた場合、ディスクのフラッシュも行われますこれが最も安全な方法です
    ここに画像の説明を挿入

  • トランザクションがコミットされると、REDO ログ バッファの内容のみが同期せずにページ キャッシュに書き込まれます。いつディスク ファイルと同期するかはシステムによって決定されます。この方法の方がパフォーマンスは高くなりますが、SQL サービスが停止してもデータは消えません。ただし、システムのダウンタイムや停電などによりデータが消失する可能性はあり、依然として信頼性は高くありません。
    REDO ログは物理ログを記録します。つまり、ディスク上の特定の場所の変更を記録します。そのため、データを復元するとき、コマンドを実行する必要がないため、コマンドよりも高速になります。

REDOログの利点

  • ディスクのリフレッシュ頻度を減らします。これを行わないと、トランザクションがコミットされるたびにダーティ ページがディスクに書き込まれます。
  • ディスクへの連続書き込み、高速フラッシュ
  • メモリとディスクの 2 つの部分に分かれており、メモリ部分ではトランザクションの実行を継続的に記録することができ、最終的にはディスクに保存されます。

データはメモリ内で変更されるため、 redo を使用してすぐにディスクに更新する必要はありませんが、REDOのサイズには制限があり、バックアップを超えると上書きされるため、メモリ サイズには制限があります。メモリ内のデータをいつディスクにフラッシュする必要がありますか?

CheckPoint チェックポイント メカニズム

その目的は、特定の時点でメモリのダーティ ページをディスクにフラッシュすることです。2つのチェックポイント間のデータをすべてREDOで記録しておかないとデータのバックアップが不完全となり、
事故発生時にチェックポイントを完全に復元できなくなります。

  • Sharp checkpoint:データベースが正常にシャットダウンしたときに全部メモリ内をディスクにフラッシュします
  • Fuzzy checkpoint: データベースの実行中は、すべての更新がパフォーマンス上の問題を引き起こすため、部分ダーティ ページがさまざまなタイミングでディスクにフラッシュされます。
    トリガーのタイミングは 4 つあります。
  1. Master Thread Checkpoint: スケジュールされた更新。指定された間隔でマスター スレッドによって异步ディスクに更新されます。
  2. FLUSH_LRU_LIST Checkpoint内存不足:バッファ プールの使用時に、LUR ページ置換アルゴリズムを使用して、page cleaner最も最近使用されていないページをディスクにフラッシュします。
  3. Async/Sync Flush Checkpoint:redo 日志空间不足このとき、page cleaner一部のダーティ ページがディスクにフラッシュされるため、REDO で記録された関連するダーティ ページ レコードが解放され、新しいデータが記録されます。しかし、同期と非同期はどうなるでしょうか? REDOログの残り容量に依存
  4. Dirty Page too much: メモリ内のダーティ ページの割合が大きすぎる場合、ダーティ ページはマスター スレッドによってディスクにフラッシュされます。バッファプールに十分なスペースがあることを確認してください

データ セキュリティの場合、ディスクへの更新が遅くなるほどパフォーマンスが向上します。この観点から、トリガー メカニズムに到達するチェックポイントができる限り少なくなるように、SQL サービスのパフォーマンスを最適化します。
バッファプールのメモリ容量を増やし、REDOログの容量を増やす必要があります。
しかし、メモリ内のダーティページをディスクにフラッシュする際、データに対応するディスクを直接操作してランダムIOを行うのでしょうか?いいえ、中間バッファがあります

Double Writer ダブルライトディスク

なぜ二重書き込みと呼ばれるのでしょうか?メモリ上のデータはまず顺序ディスク上の二重書き込みバッファに書き込まれるため、書き込みが成功したことを確認した後、メモリ上の同じデータを离散対応するテーブルスペースに書き込みます。

なぜ二重書き込みメカニズムがあるのですか?
まず、二重書き込みメカニズムの欠点について説明します: 同じデータがディスクに 2 回書き込まれ、追加の IO が実行されます。書き込みはシーケンシャルですが、追加の IO により全体のパフォーマンスが 5 ~ 10% 低下します。
二重書き込みメカニズムが導入された理由について話しましょう。根本的な理由は、メモリ データをリフレッシュするプロセスで発生するページ破損の問題、つまり部分写问题
ページ破損の問題を解決するためです。InnoDB エンジンはページを単位として使用し、各ページのサイズは 16K です。オペレーティング システム ( Linux) もページを単位として使用しますが、そのサイズは 4K です。つまり、メモリ内のデータをディスクに更新する場合は、まずオペレーティング システムのページに渡され、その後オペレーティング システムのページに渡される必要があります。ディスクに更新されます。これは、メモリ データのページが更新されるたびに、そのデータをオペレーティング システムに 4 回書き込む必要があることを意味します。しかし、この 4 回の間にダウンタイムなどの事故が発生すると、メモリ内の不完全なページがディスクに書き込まれ、ページ破損が発生します ( 部分写问题)。現時点では、永続性の原則に違反することはできません。ダウンタイムなどによりディスクに正しく書き込まれなかったデータをどのように回復するか?
まず最初に考えたのは、メモリ データがディスクに書き込まれ、REDO ログが存在することを確認することでした。しかし、REDO ログには何が記録されるのでしょうか? 物理レコード、つまりオフセットがxxxであるxxxページのアドレスデータはxxxxですが、ダウンタイムの原因となったレコードのページが完全に書き込まれておらず、ページが破損しているため、データレコードが壊れていることを意味します已经损坏!。対応するページアドレスも無効となります。では、REDO では binlog は使用できないのでしょう
か? binlog の機能はデータのバックアップとマスター/スレーブ レプリケーションですが、メモリとディスクのデータにはまったく注目せず、どれがディスクに書き込まれ、どれがダーティ ページにあるかを区別できません。
フルリカバリを行えば大丈夫なようです。しかし、コストが高すぎて現実的ではありません。
このとき、書き込みプロセス中に事故が発生した後にやり直しに記録されたページが破損して無効になることを防ぐために、二重書き込みメカニズムが導入されています。つまり、やり直しログに記録されているページが最初に操作されないということです。 。
では、最初にデータをどこに書き込むか? 書き込む場所はディスク上になければ意味がありません。なぜなら、私たちが行っているのはメモリとディスク間の対話型バックアップだからです。
InnoDB エンジンは、注文が最初にシステム テーブル スペースに書き込まれ、成功後に対応する独立したテーブル スペースに個別に書き込まれることを示します。また、InnoDB は二重書き込みの書き込み速度を最適化します。これは、この方法での書き込みはシーケンシャル書き込みであり、IO 操作が 1 つ追加され、それによるパフォーマンスの損失も最小限に抑えられるためです。
以上が導入理由です双写机制


二重書き込みファイル
このメカニズムでは、対応するストレージ ファイルが存在する必要があります。
二重書き込みキャッシュには、メモリ内の部分とディスク内の部分という共有部分があります。各部分は 2M に固定されており、
二重書き込みバッファーはディスク表スペース上の 128 ページ、つまり 1M ずつの領域が 2 つあり、合計 2M になります。バッファプールのデータをコピーする場合は、まず二重書き込みキャッシュのメモリ部分にデータをコピーし、次にメモリ部分を2回に分けて、ディスク部分の二重書き込みファイルを最初に書き込みます。毎回サイズ 1M のファイルを二重に書き込み、書き込みが成功した後、同じデータを対応する独立した表スペースに個別に書き込みます。


メモリがチェックポニット メカニズムをトリガーすると、いくつかのページのディスクへの書き込みが開始されます。具体的なプロセスは次のとおりです。

  1. メモリに書き込むダーティページをコピーして double writeメモリ部にコピー
  2. 二重書き込みメモリ部分は一度に 1 つの領域、つまり 1M に書き込まれ、システムテーブルスペース部分には 2 つの順序で書き込まれます。
  3. 書き込みが成功したことを確認した後、二重書き込みのメモリ部分は、対応する独立したテーブルスペースに同じデータを離散的に書き込みます。

二重書き込みプロセスで事故が発生した場合はどうなりますか?
最初の書き込みで事故が発生しました: 1 回目は、バックアップのためにメモリ データ (二重書き込みメモリ バッファ) をシステム テーブル ファイルに書き込むことです。
このプロセスで事故が発生した場合、 REDO を使用して、2 番目の書き込み時の 2 番目の事故を直接復元できます: 2 番目の書き込みでは、メモリ データ (二重書き込みメモリ バッファ) を対応する独立したテーブル スペースに書き込みます。ディスクへの書き込み中に事故が発生すると、ページが破損し但redo记载的页没有发生损坏ます。となり、やり直しで記録したページもこの時点で無効となります。ただし、ファイルがシステム テーブルスペースにある場合、システムの再起動時に、プロセス ページが損傷していないかどうかがチェックされ、ページが破損していることが判明した場合は、システム テーブルスペースから復元されますやり直しページが正常に書き込まれた後でのみ、ビジネスが完了したとみなされるため、事故です。


ここに画像の説明を挿入

アンドゥログ ロールバックログ

REDO ページはトランザクションの通常の送信のみを処理でき、トランザクション中にロールバックまたは手動ロールバックを引き起こす例外が発生した場合、トランザクションがオープンされる前のデータは復元できません。SQLの実行後にメモリ内のデータが変更されているため、REDOレコードはすべてディスクデータの変更です。このとき、undo logロールバックログとしてログが必要となり、SQL文実行後、元のデータに戻す指示をロールバックログに記録する必要があります。たとえば、id=2 のデータの挿入、id=2 の削除は、アンドゥ ログに記録される必要があります。ただし、これは完全な SQL ではなく、疑似 SQL のようなものです。ロールバック実行後は、トランザクションのアンドゥログで逆の操作を行うことで復旧を実現します。メッセージの原子性と一貫性を確保します。
アンドゥログは、実際に変更され、実際の場所が変更されたデータを復元する論理ログを記録します。
ここに画像の説明を挿入

では、なぜ論理ログを使用するのでしょうか? ロールバックログは、データが増加していても、元のデータにそのデータが存在しない場合、以前の状況を記録することができません。さらに、MVCC の ReadView に論理ログを記録すると便利な場合があります。
アンドゥログログ機能

  • アンドゥログはデータをロールバックでき、リバースSQLを実行することで以前のデータ状態に戻すことができます。
  • アンドゥ ログは、データの履歴バージョン (MVCC) を記録するために使用できますが、これは変更および削除操作用であり、レコードの書き込み操作の場合、トランザクションがコミットされると機能が失われます。そして削除されます。

保存を元に戻す

  • UNDOログはセグメント単位で管理されます回滚段各ロールバック セグメントには 1024 のロールバック ページがあり、
  • 各トランザクションは 1 つのロールバック セグメントのみを使用しますが、ロールバック セグメントは同時に複数のトランザクションを処理できます。
  • 各トランザクションはロールバック セグメントを開きます

トランザクションがコミットされると、アンドゥ記録に従ってMVCCに使用され、そうでない場合はリサイクルのためにパージされ、ページが次のトランザクションで使用可能かどうかが判断されます

ロック

ロックは、複数のスレッドが限られたリソースをめぐって競合する場合、割り当て制御に使用されます。データベース内のスレッドの安全性の問題を防止し、各スレッドが秩序ある方法でリソースを取得できるようにするには、ロックが必要です。
これはマークの一種であり、錠前ごとに異なるマークが使用され、コードはマークごとに異なる処理方法に対応しています。
InnoDB エンジンは、より細かい粒度を備えた行レベルのロックであるため、ロックの範囲が小さくなり、データ操作時の同時実行量が増加します。ただし、対応する行データがインデックスから見つからない場合は、テーブル レベルのロックが形成されます。スレッドがデータ行を操作する場合、テーブル全体をロックする必要があるため、システムの同時実行性が大幅に低下します。

ロックは機能に応じて读锁とに分けることができます写锁データベース内のすべてのスレッド関数は合計に分割できます。複数のスレッド間でデータをまとめて読み取る場合は問題ありませんが、書き込まれているデータの一部を読み書きすると、この問題が発生します。最も深刻なことは脏读、書き込み中のデータはダーティ ライトとなり、データの混乱が生じます。したがって、読み取り時には読み取り用のロックを追加してください。ロックを読み取るときは、読み取りデータに書き込みロックがないことを確認してください。このロックは書き込みを防ぐために使用されますが、読み取りは可能です。各スレッドは読み取りロックを解放します。読む。書き込み時に書き込みロックを追加します。ロックするときは、データに読み取りロックと書き込みロックがないことを確認してください。このロックは、書き込み時に他のスレッドが書き込みまたは読み取りできないようにするために使用されます。ロックを書き込むときは、スレッドが操作を実行します。操作が完了するとロックが解除されます。
つまり、読み出し時は読み取れるが書き込み時は書き込み不可、読み出し時は書き込み不可、書き込み時は読み取れないというライトロックとリードロックの連携により、ダーティライト、ダーティリードを解決することができます
。 (読み取り時ロック、ロック時ロック) 前後に書き込みロックは不可、ロック追加後は書き込みロック不可)。


ロックの 2 つのタイプがわかったので、アクションの範囲に応じて、ページ レベルのロック、テーブル レベルのロック、行レベルのロックの 3 つのタイプに分類できます。このカテゴリでは、特定の機能に応じてさらに細分化できます。InnoDB エンジンでは、行レベルのロックが最も一般的に使用されるのは明らかです。

実際、ロックは、単純な読み取り/書き込みロック (テーブル読み取り/書き込みロック、行レベルの読み取り/書き込みロックなどのテーブル構造を無視し、テーブル内のデータの同時実行セキュリティのみに重点を置く) と非単純なロックに分けることもできます。ロック。

テーブルロック

テーブル読み取りロック、テーブル書き込みロック

マルチスレッド データの読み取りおよび書き込みが行われる場合、エンジンがテーブル全体をロックすると、読み取り時にテーブル全体の読み取りロックが開かれるため、テーブル全体のデータの各行は読み取りのみ可能になります。書く。データを挿入するとき、テーブル全体を他のスレッドで書き込んだり、他のスレッドで読み取ったりすることはできません。
ロック中:

  • テーブル写锁: lock tables 表名 write: テーブル全体は、ロックが解放されるまでは、どのスレッド (スレッド自体も含む) によって読み取りのみ可能ですが、書き込みはできません。ロックが失敗すると、このテーブルに書き込みロックがかかる可能性があります。
  • テーブル读锁: lock tables 表名 read: このスレッドのみがテーブル全体の書き込みと読み取りを行うことができ、他のスレッドは書き込みも読み取りもできません。ロックが失敗すると、テーブルに読み取りロックと書き込みロックが発生する可能性があります。

トランザクションの分離はロックによって自動的に制御され、手動でロックしたりロックを解放したりしてもトランザクションとは何の関係もありません。
ロックを解除するには、コマンドを入力する方法unlock tables;と直接ロッカーを切断する方法の 2 つがあります。

メタデータ ロック MDL

メタデータ ロックは、テーブル内のデータの読み取りおよび書き込み時にテーブル構造が変更できないようにするために、エンジンによって自動的に追加されます。DDL ステートメントと DML および DQL の間の競合を回避します。
テーブル レベルの読み取りロック、書き込みロック、行レベルの読み取りロックと書き込みロックは、それらが配置されているテーブルにメタデータ ロックを自動的に追加します。メタデータ ロックは DDL ステートメント用です。MDL ロックでは、DDL 操作は実行できません。ただし、 , DML ロックは、データ操作の読み取り/書き込みロックに対して透過的です。DML はミューテックス (書き込みロック) でもあり、このロックがテーブルに存在する場合は追加できません。
MDL ロッカーの 1 つは DML と DQL であり、もう 1 つは DDL です。MDL ロックを通じて相互排他関係を形成するのは彼らです。

インテントロック

インテンション ロックは、テーブル レベルのロックと行レベルのロックの間の競合に対処するためのマークに似ています。書き込みのためにテーブルをロックする必要がある場合、テーブルにテーブルレベルの書き込みロックまたは読み取りロックがないこと、およびテーブル内のデータの各行に読み取り/書き込みロックがないことを確認することは困難です。各行の状況を知りたい場合は、各行を走査して確認する必要があり、ロックの効率に影響を与えるためです。各行がロックされている場合、その行が配置されているテーブルにインテント ロックが自動的に適用され、この行にロックがあることがマークされます。これにより、テーブルが DDL 操作または書き込みロックを実行するときに、ロックが存在するかどうかを直接判断できます。インテントロック。
同様に、テーブルに読み取りロックを設定する場合は、テーブルに行レベルの読み取りロックを含めることを許可しますが、書き込みロックは許可されません。テーブルに書き込みロックを設定すると、どの行にも読み取りロックを含めることはできません。ロックと書き込みロック。
したがって、意図ロックも共有ロック (読み取りロック) と排他ロック (書き込みロック) に分けられます。行データには行レベルの読み取りロックのみがあり、インテント ロックは共有ロックであり、テーブル レベルの書き込みロックと相互排他的です。行データに書き込みロックが含まれている限り、このインテント ロックはtable は排他ロックであり、テーブル レベルの読み取りロックとは異なり、書き込みロックは相互に排他的です。
テーブル インテント ロックは、行レベルのロックと対応するテーブル レベルのロック間の相互排他を表し、テーブルをロックするときに行ロックのロック状態を 1 つずつ確認する必要がなくなり、ロックの効率が向上します。

行レベルのロック

行読み取りロック、行書き込みロック

機能はテーブルロックと同じですが、範囲をテーブル全体からテーブル内の特定のデータ行に絞ることで、データ操作時にロックが解除された部分を他のスレッドで操作できるようになり、全体の同時実行性が向上します。高いですが、一定要同过索引来检索数据

ギャップロック

プロキーロック

MVCC

多版本并发控制(Multi-Version Concurrency Control、略して MVCC) は、データの複数のバージョンを維持します。このメカニズムにより、現在のデータの各トランザクションの前に特定のデータ、つまり履歴バージョンを取得できます。たとえば、id=2 および name='Zhang San' の新しいデータがテーブルに挿入され、次に id=2 のデータが変更されて name='Li Si' になると、name= の元のデータが変更されます。 「zs」は上書きされますが、アンドゥログには現在のデータを name=「Zhang San」に戻す方法が記録されています。このとき、名前が「Li Si」に変更されたトランザクションがコミットされていない場合、トランザクション内で名前が「Li Si」のデータは、リードコミット以上の分離レベルで読み取ることができません。を実現するには、当前读スレッド間の待機を減らし、同時実行性を向上させるために、このトランザクションの前の最新のデータ、つまり「Zhang San」という名前のデータを読み取ることができます。この道は です快照读スナップショット読み取りは、現在の操作スレッドの書き込みデータ ロックによって引き起こされる他のスレッドの読み取りブロックを解決します但写操作依然需要等待反復可能な読み取り分離レベルの下にある場合、読み取りデータは最新のコミットではない可能性があります。

実現原理

MVCC の実装は分離できませんテーブルを作成するとき、表示および設計するフィールドに加えて、InnoDB エンジンはシステムの内部でのみ使用される 2 つまたは 3 つの隠しフィールドを追加します表中隐藏的两个字段では、なぜその数が定かではないのかというと、そのうちの 1 つは、主キーを指定するとシステムはこのフィールドを作成しない、そして現時点では隠しフィールドが 2 つしかない、ということがよく言われるからです。以下は、テーブル ファイルで表示できるこれら 3 つの隠しフィールドの詳細な紹介です。undo logReadView
隐藏字段
rowId

  • DB_TRX_ID: 最後に変更されたトランザクション ID、このレコードに挿入されたレコード、または最後に変更されたレコードのトランザクション ID。
  • DB_ROLL_PTR: ロールバック ポインタ。アンドゥ ログはこのデータの前のバージョンを指し、前のバージョンのこのフィールドは再び前のバージョンを指し、バージョン チェーンを形成します。
  • DB_ROW_ID: テーブルに主キーがない場合、主キー インデックスを生成するために、システムは自動的にフィールドを作成し、各データはインデックス キーとして一意の非 null 値を生成します。主キーが指定されている場合、フィールドは作成されません。

隠しフィールドによると、各データには最新の現在のトランザクション ID が記録され、そのバージョン チェーンは、
各データが論理レコードに復元されたかどうかを記録する Undo ログによりデータの回復に使用されることがわかります。最後にコミットされたトランザクションの後、同じです。前のバージョンでは、前のバージョンに復元する方法も記録されます。このように、論理的には、各データのバージョン チェーンが記録されているように見え、各データはそのバージョンを指します。前のバージョン。DB_ROLL_PTRバージョン チェーンの最初のアドレスは、バージョン チェーンと現在のデータを接続する隠しフィールドに保存されます。現在のデータのフィールドからDB_ROLL_PTRポインタに沿って移動して、データの各トランザクション バージョンを見つけることができます。

他のスレッドが現在のデータのトランザクションを開いたときのみ、以前のデータは履歴バージョンを形成し、新しいデータ (送信されていない場合でも) は元に戻すときにそのアドレスを指します。新しいデータのトランザクション ID フィールドには、現在開いているトランザクション ID。元に戻すログは、トランザクションの送信には注目せず、トランザクションの開始とロールバックにのみ注目します。
デモ

  1. 新しいデータを挿入するここに画像の説明を挿入
  2. 新しいデータが送信された後、他のスレッドがトランザクションを開始し、このデータを変更し、経過時間を 3 に変更しますが、データが送信されていないか送信されている場合は、
    ここに画像の説明を挿入
  3. このデータが継続的に変更されると、さらに履歴バージョン情報が形成されます。このデータの履歴バージョンは、元に
    ここに画像の説明を挿入
    戻すログに記録されます。各バージョンには、独自のバージョンのデータのトランザクション ID とロールバック ポインタがあります。履歴バージョンのデータは論理的なものにすぎず、実際に記録されるのは、そのようなデータが復元されたかどうかです。

各フィールドには、このデータに対する最新の操作 (コミットされたかコミットされていない可能性があります) のトランザクション ID とその履歴バージョンがありますが、他のトランザクションがこのデータのどのバージョンを読み取りたいかを判断するにはどうすればよいでしょうか?

ReadView
readView は读视图、あるトランザクションが MVCC を実行するための基礎となり、どのバージョンのデータを読み込むべきかを判断するために使用されます。

ReadView には 4 つのコア フィールドが含まれます:備考: アクティブなトランザクション: オープンされているがコミットされていないトランザクション。トランザクション ID は自動で増加します。

  • m_ids: 現在アクティブなトランザクション ID のコレクション
  • min_trx_id: 最小のアクティブなトランザクション ID
  • min_trx_id: 現在の最大トランザクション ID+1 は存在しません。これは、次に開かれるトランザクションのトランザクション ID です。
  • creator_trx_id: MVCC のトランザクション ID を読み取るには、すべてが围绕这个id的事务要读取数据

テーブルのトランザクション ID 統計、履歴バージョン チェーン、および現在のトランザクション ID があれば、残りは読み取りルールです。つまり、どのトランザクションがどのバージョンのデータを読み取ることができるか、現在のトランザクション ID はこのルールに従って読み取られます
。満足したオプションを 1 つずつ比較します。どれも満足できない場合は、バージョンと 1 つずつ比較します。
ここに画像の説明を挿入

データが読み取り可能かどうかを判断するために使用されるフィールドm_idsmin_trx_idmax_trx_idcreator_trx_idDB_TRX_ID

状態 読めるか 説明する
対応するトランザクション ID== 行で読み取るトランザクション ID はい このデータは ID によって変更されており、読み取ることができます
行内の対応するトランザクション ID < 最小トランザクション ID はい このデータを操作するトランザクションはコミットされており、読み取ることができます
行内の対応するトランザクション ID>= 最大トランザクション ID+1 いいえ この時点で各スレッドは読み込み時にReadViewを生成するため、ReadViewでカウントされるデータはタイムリーではない可能性がありますが、ロウデータ内のトランザクションIDは最新のものとなります。行トランザクション ID が統計トランザクションの最大値より大きい場合は、このスレッド トランザクションよりも大きい必要があります。つまり、このスレッドが読み取る前に、他のスレッドが開いていますが、このデータは操作されているため、このデータは読み取ることができません。トランザクションがコミットされたもののまだ読み取りできない場合にも問題がありますが、その可能性は非常に低いです。
最小トランザクション ID <= 行内の対応するトランザクション ID <= 最大トランザクション ID+1 不確かな ID が ids にない場合、トランザクションは遅く開かれて送信されましたが、ids にある場合、このテーブルのトランザクションは現在開いており、アクセスできません。

各行のデータを 1 つずつ比較する必要があり、満足できない場合は次の行を比較します。

現在の行データのトランザクション ID > ReadView に記録された最大トランザクション ID
ここに画像の説明を挿入

提出された RC の詳細な原​​則を読む

読み取りは送信されました。スレッドが動作するとロックが書き込まれ、他のスレッドは MVCC のみを実行できます。問題は、繰り返し読み取りできない可能性があることです。読み取りごとに ReadView が生成されるため、2 つの読み取りの間には時間間隔があり、ReadView 内のデータが変更されます。たとえば、操作中のデータが行トランザクション id>max_trx_id を満たす場合、このスレッドはこのバージョンのデータを読み取ることはできませんが、一度読み取ると、同じ ID が id<min_trx_id を満たすため、このバージョンのデータは読み取り可能になります。これにより 2 つの読み取りに矛盾が生じ、再現不可能な問題が発生します。
ここに画像の説明を挿入

繰り返し読み取りRRの詳細原理

反復不可能な読み取りは、読み取りごとに生成される ReadView が異なるためです。最初の読み取りの ReadView が各読み取りで使用される場合、各読み取りのルールは同じであり、読み取られるデータも同じです。

ここに画像の説明を挿入
トランザクションでは毎回同じデータが読み取られますが、最新の送信データの読み取りが犠牲になります。最新の送信データは実際のものですが、読み取りはまだ過去のものであり、MVCC が返す書き込み操作では使用できません。現実となり、ファントム・リーディングが発生する可能性があります。それについてはすでに上で紹介したので、ここでは繰り返しません。


ノンリピータブル読み取りとファントム読み取りは MVCC メカニズムを使用して生成され、ノンリピータブル読み取りは分離レベルの読み取りコミット ステージにあり、ファントム読み取りは分離レベルのリピータブル読み取りステージにあるため、(RC のみ) )、( 读已提交阶段RR 读未提交阶段) MVCC のみを使用する


MVCC は私にとって平行空間のように感じられますが、平行空間でしか体験できませんが、変更することはできません。変更すると、現在の空間が変化します。したがって、変更したい場合は、現在のスペースに戻ることしかできません。例: 履歴バージョンで ID 2 のデータを削除しましたが、現在のトランザクションで ID 2 のデータを変更しようとしたときに、そのデータが消えていることがわかりました。

ここに画像の説明を挿入
これと同様に、正方形はデータの行を表し、各層はその各バージョンを表し、ReadView に従ってどのバージョンを読み取るかを決定します。

おすすめ

転載: blog.csdn.net/m0_52889702/article/details/128403946