MySQL のロック、トランザクション、MVCC についての簡単な理解

coldplay.xixi
リリース: 2020-11-04 17:31:12
転載
2351 人が閲覧しました

mysql チュートリアル このコラムでは、MySQL のロック、トランザクション、MVCC について簡単に理解します。

MySQL のロック、トランザクション、MVCC についての簡単な理解

#その他の関連する無料学習の推奨事項:

mysql チュートリアル (ビデオ)

次の内容は、「High-Performance MySQL」(第 3 版)からの抜粋です。

MySQL は自動を使用します。」デフォルトでは送信 ( AUTOCOMMIT) モードです。つまり、トランザクションを明示的に開始しない場合、各クエリはコミット操作を実行するトランザクションとして扱われます。現在の接続では、自動コミット モードを有効または無効にするには、 AUTOCOMMIT 変数

#MySQL はトランザクションに ACID をどのように実装しますか? トランザクションには ACID の 4 つの主要な特性があります。では、MySQL はトランザクションのこれら 4 つの属性をどのように実装するのでしょうか?

    原子性 すべてが成功するか、すべてが失敗するかのどちらかです。 MySQL は、undo_log を記録することでアトミック性を実現します。 undo_log は
  • ロールバック ログ
    で、実際の SQL が実行される前にディスクに書き込まれ、データベース データが操作されます。例外またはロールバックが発生した場合は、undo_log に基づいて逆の操作を実行して、トランザクションが実行される前の状態にデータを復元できます。

  • 永続性 トランザクションが正常にコミットされると、データベースへの影響は永続的になります。この時点でシステムがクラッシュしても、変更されたデータは失われません。 InnoDBはMySQLのストレージエンジンであり、データはディスク上に保存されますが、データの読み書きに毎回ディスクIOが必要となると効率が非常に悪くなります。このため、InnoDB はデータベースにアクセスするためのバッファーとしてキャッシュ (バッファー プール) を提供し、データベースからデータを読み取るときは、まずバッファー プールから読み取ります。バッファー プールにない場合は、それが読み込まれます。データをディスクからバッファ プールに書き込みます。データベースにデータを書き込むときは、最初にバッファ プールに書き込まれ、バッファ プール内の変更されたデータが定期的にディスクに更新されます。

    このような設計では、対応する問題も発生します。データが送信され、データがまだバッファ プール内にある場合 (ディスクがまだフラッシュされていない場合)、MySQL がダウンしたり電源が失われた場合はどうすればよいでしょうか?データは失われますか?

    答えは「いいえ」です。MySQL は redo_log メカニズムを通じて耐久性を保証します。 redo_log は redo log です。簡単に言うと、データが変更されると、バッファー プール内のデータの変更に加えて、操作も redo_log に記録されます。トランザクションが送信されると、fsync インターフェイスが記録されます。 redo_log. プレートをフラッシュするために呼び出されます。 MySQL がダウンした場合、redo_log 内のデータを読み取り、再起動時にデータベースを回復できます。

  • #分離

    分離は ACID で最も複雑なもので、分離レベルの概念が含まれており、合計 4 つあります

    • コミットされていない読み取り
    • コミットされた読み取り
    • 繰り返し可能な読み取り
    • Serializable

    簡単に言えば、分離レベルは、トランザクション内のデータの変更、どのトランザクションが表示され、どのトランザクションが表示されないかを規定します。分離とは、複数の同時読み取りおよび書き込み要求のアクセス シーケンスを管理することです。

    MySQL 分離の具体的な実装については後で説明します。

  • 一貫性

    同時環境でのロールバック、リカバリ、分離を通じて一貫性を実現します。

トランザクションの同時実行によって発生する可能性のある問題

前述の説明を通して質問、I 複数の SQL の同時実行であっても、複数の SQL を含む手動で編成された複数のトランザクションの同時実行であっても、単一の DDL 実行がトランザクションとして自動的に送信されることがわかっていると、トランザクションの同時実行の問題が発生します。

具体的には:

  • ダーティ ライト (あるトランザクションによって送信されたデータは、別のトランザクションによってコミットされていないデータを上書きします)
  • ダーティ リード (あるトランザクションによって読み取られます)別のトランザクションからのコミットされていないデータ)
  • ##繰り返し不可能な読み取り (重要な点は、トランザクション内で複数回読み取られるデータの更新と削除は異なるということです)
  • ファントム読み取り (重要な点は、挿入トランザクションで複数回読み取られるレコードの数が異なるということです)
上記のトランザクションの分離レベルについては、すべての分離レベルについて説明しました。 MySQL ではダーティ ライトが発生しないことを保証できるため、残る問題はダーティ リード、非反復読み取り、ファントム リードだけです。

各分離レベルが上記の問題をどのように解決するか、または解決しないかを詳しく見てみましょう:

Read uncommitted

Uncommitted read. このレベルは、読み取りプロセス中にロックを追加しません。書き込みリクエスト中にのみロックされます。したがって、書き込み操作により読み取り中にデータが変更されます。ダーティ リードが発生します。反復不可能な読み取りとファントム読み取りが自然に発生します。

コミットされた読み取り

コミットされた読み取りは、コミットされていない読み取りと同様、読み取り用にロックされず、書き込み用にもロックされません。違いは、ダーティ リードの問題を回避するために MVCC メカニズムが使用されることですが、ノンリピータブル リードやファントム リードの問題もあります。 MVCC については後ほど詳しく説明します。

MySQL のデフォルト分離レベル。このレベルでは、MySQL は問題を解決するために 2 つの方法を使用します

  1. 読み取り/書き込みロック 並列読み取り時に読み取りロックが追加され、読み取りと読み取りでロックが共有されます。 書き込み要求がある限り、書き込みロックが追加されるため、読み取りと書き込みはシリアルになります。 ロックはデータの読み取り時に実行され、他のトランザクションはデータを変更できません。したがって、反復不可能な読み取りは発生しません。 データの変更や削除にもロックが必要ですが、他のトランザクションがデータを読み取ることができないため、ダーティ リードは発生しません。 最初の方法は、「悲観的ロック」 とよく呼ばれるものです。データはトランザクション プロセス全体でロックされます。これは比較的保守的で、パフォーマンスのオーバーヘッドが比較的大きくなります。
  2. MVCC (後述)

また、Next-Key ロックを使用することで、ファントム読み取りの問題もある程度解決できます。 。これについては後で説明します。

シリアル化可能

この分離レベルでは、トランザクションはシリアルに実行されます。 自動コミットが無効になっている場合、InnoDB はすべての通常の SELECT ステートメントを暗黙的に SELECT ... LOCK IN SHARE MODE に変換します。つまり、読み取り共有ロックが読み取り操作に暗黙的に追加されるため、ダーティ読み取り、反復不可能な読み取り、ファントム読み取りの問題が回避されます。

MVCC

マルチバージョン同時実行制御 (MCC または MVCC) は、データベースへの同時アクセスを提供するためにデータベース管理システムによって一般的に使用される同時実行制御方法であり、トランザクション メモリ (MCC または MVCC) を実装するためにプログラミング言語で使用される同時実行制御方法です。データベースを構築し、プログラミング言語でトランザクション ストレージを実装します。

簡単に言えば、これはデータベースが同時実行性を制御するために使用する方法です。各データベースには、MVCC の実装が異なる場合があります。

一般的に使用されている MySQL を例に挙げると、MySQL の InnoDB エンジンは MVCC を実装しています。

MVCC はどのような問題を解決できますか?

上記の定義から、MVCC は主にトランザクションの同時実行性を解決することがわかります。データの一貫性の問題

InnoDB による MVCC の実装方法

次の図は、「High Performance MySQL」(第 3 版) からのものです

この本はよく書かれ、よく翻訳されています。私の最初の MySQL の体系的な理解も、この本を読んだおかげです。しかし、個人的には、MVCC の実装方法の説明にはいくつかの問題があると思います。

問題がどこにあるのか見てみましょう

  • まず、MySQL の公式ドキュメントを見てみましょう。 5.6、および 5.7。document[1] の 2 つのバージョンにおける MVCC のこの部分の説明は、ほぼ同じです。

ドキュメントによると、各データに 3 つの非表示列が追加されていることは明らかです。

    6 バイトの DB_TRX_ID フィールドは、レコードの最新の挿入または更新のトランザクション ID を示します。
  • 7 バイトの DB_ROLL_PTR フィールドは、レコードのロールバック セグメントの UNDO ログ レコードを指します。
  • 6 バイトの DB_ROW_ID は、新しいデータが挿入されると自動的に増加します。テーブルにユーザーの主キーがない場合、InnoDB は DB_ROW_ID フィールドを含むクラスター化インデックスを自動的に生成します。
  • ここで、ロールバック セグメントを含む MySQL の内部構造図を追加します。

バージョン チェーン

その前に、 undo_log の概念について話しました。各 undo ログには、roll_pointer 属性があります。その後、すべてのバージョンは、roll_pointer 属性によってリンク リストに接続されます。このリンク リストをバージョン チェーンと呼びます。バージョン チェーンのヘッド ノードは、最新の値です現在のレコードの。

読むビュー

カラムとバージョン チェーンを非表示にすることで、MySQL はデータを指定されたバージョンに復元できますが、具体的にどのバージョンに復元するかは ReadView に基づいて決定する必要があります。いわゆる ReadView とは、トランザクション (トランザクション A として記録される) がある瞬間にトランザクション システム全体 (trx_sys) のスナップショットを取得することを意味し、後で読み取り操作が実行されるときに、読み取られたデータ内のトランザクション ID と比較されます。 trx_sys スナップショットを使用して、データが ReadView に表示されるかどうか、つまりトランザクション A に表示されるかどうかを判断します。

これまでのところ、MVCC は隠しフィールド、undo_log チェーン、ReadView に基づいて実装されていることがわかりました。

Read commit の MVCC

Read commit 分離レベルで MVCC を使用するソリューションについては前に説明しました。ダーティリードの問題。 ここでは 2 つの記事を参照します:

  • https://cloud.tencent.com/developer/article/1150633
  • https:/ / cloud.tencent.com/developer/article/1150630

InnoDB は、バージョンが現在のトランザクション バージョンよりも古いデータ行のみを検索します (つまり、行のバージョン番号はトランザクション バージョンのシステム バージョン番号以下)、これにより、データによって読み取られた行がトランザクションの開始前にすでに存在しているか、トランザクション自体によって挿入または変更されたことが保証されます。 したがって、ダーティリードは発生しません

読み取りがコミットされました 分離レベルでの反復不可能な読み取りの発生は、読み取りビューの生成メカニズムが原因です。 Read commit レベルでは、現在のステートメントが実行される前にコミットされたデータが表示されます。各ステートメントの実行中に、読み取りビューが閉じられ、現在の読み取りビューが再作成されます。このようにして、読み取りビューのトランザクション間隔は、現在のグローバル トランザクション リストに基づいて作成できます。簡単に言えば、読み取りコミット分離レベルでは、MVCC は選択ごとにスナップショット バージョンを生成するため、各選択では異なるバージョンのデータが読み取られるため、したがって反復不可能な読み取りが発生します

#Repeatable read の MVCC

Repeatable Read 分離レベルは、トランザクション内の繰り返し不可能な読み取りの問題を解決します。複数の読み取り異なる結果が生成されることはなく、反復可能な読み取りが保証されます。前回の記事では、Repeatable readには2つの実装方法があり、1つは悲観的ロック方法、その逆のMVCCは楽観的ロック方法であると述べました。

反復可能な読み取り分離レベルによって反復不可能な読み取りの問題が解決できる根本的な理由は、実際には、読み取りビューの生成メカニズムがコミットされた読み取りの生成メカニズムと異なるためです。

  • 読み取りコミット済み: 現在のステートメントが実行される前にデータが送信されている限り、データは表示されます。
  • 反復読み取り: 現在のトランザクションが実行される前にデータが送信されている限り、データは表示されます。

コミットされた読み取りとは異なり、反復読み取りの分離レベルでは、トランザクションが作成されると、現在のグローバル読み取りビューが生成され、トランザクションが終了するまで維持されます。これにより、反復読み取りが可能になります。

#ファントムリードとネクストキーロック

現在のリードとスナップショット読み取り

MVCC メカニズムを通じて、データは繰り返し読み取ることができますが、読み取るデータはデータベースの現在のデータではなく、過去のデータや時期外のデータである可能性があります。データ!この履歴データの読み取り方法を

snapshot read (スナップショット読み取り) と呼び、現在のバージョンのデータベース データを読み取る方法を current read (現在の読み取り) と呼びます。 Reference[3]

  • スナップショットの読み取り: select
    • select * from table….;
  • 現在の読み取り: 特殊な読み取り操作、挿入/更新/削除操作は、現在の読み取りに属し、現在のデータを処理し、ロックする必要があります。
    • select * from table where ? 共有モードでロック;
    • select * from table where ? 更新用;
    • 挿入;
    • 更新;
    • 削除;

現在の読書

におけるファントム リーディングの問題を解決するには、MySQL を使用します。トランザクションはネクストキーロックを使用します。

反復読み取りは、ネクスト キー ロック メカニズムによるファントム読み取りを回避します。

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

  • #レコード ロック: 単一レコードのロック
  • Gap Lock: ギャップ ロック、範囲をロックしますが、レコードは含まれません
  • Next-Key Lock: ギャップ ロック レコード ロック
    #ネクストキーロックは行ロックの一種で、レコードロック(レコードロック)、ギャップロック(ギャップロック)と同等の実装であり、レコード自体をロックする(レコードロックの機能)だけでなく、範囲をロックすることもできます (ギャップロック機能)。
InnoDB がインデックス レコードをスキャンすると、最初に行ロック (レコード ロック) がインデックス レコードに追加され、次にギャップ ロック (ギャップ ロック) がインデックス レコードの両側のギャップに追加されます。ギャップ ロックを追加すると、他のトランザクションはこのギャップ内のレコードを変更したり挿入したりできなくなります。

クエリされたインデックスに一意の属性が含まれている場合、Next-Key Lock は最適化され、範囲ではなくインデックス自体のみをロックする Record Lock にダウングレードされます。

以上がMySQL のロック、トランザクション、MVCC についての簡単な理解の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.im
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート