1.MySQLのアーキテクチャと歴史
高性能MySQLによって書かれたメモを参照してください
1.1MySQL論理アーキテクチャ
2番目の層のアーキテクトはMySQLのコア部分です。クエリ、解析、最適化、キャッシング、およびすべての組み込み関数(日付、時刻、数値、暗号化関数)を含む、ほとんどのMySQLコア関数はこのレイヤーにあります。ストレージ全体エンジンの機能は、このレイヤーに実装されています:ストアドプロシージャ、ビュー、トリガーなど。
ストレージエンジンはSQLを解析せず、異なるストレージエンジンは相互に通信せず、上位サーバーからの要求に応答するだけです。
InnoDBは例外であり、MySQLサーバー自体がこの関数を実装していないため、外部キー定義を解析します。
1.1.1接続管理とセキュリティ
各クライアントはサーバー上にスレッドを持ち、この接続のクエリはこの単一のスレッドでのみ実行され、スレッドは特定のCPUコアまたはCPUでのみ実行できます。サーバーはスレッドのキャッシュを担当するため、新しい接続ごとにスレッドを作成または破棄する必要はありません。
MySQL5.5以降のバージョンは、スレッドプールプラグインをサポートするAPIを提供します。このプラグインは、プール内の少数のスレッドを使用して、多数の接続を処理できます。
1.1.2最適化と実行
MySQLはクエリを解析し、内部データ構造(解析ツリー)を作成してから、最適化の書き換え、テーブルの読み取り順序の決定、適切なインデックスの選択など、さまざまな最適化を実行します。
オプティマイザーは、テーブルが使用するストレージエンジンを気にしませんが、ストレージエンジンはクエリの最適化に影響を与えます
SELECTステートメントの場合、サーバーはクエリを解析する前に、まずクエリキャッシュ(クエリキャッシュ)をチェックします。対応するクエリがその中に見つかった場合、サーバーはクエリの解析、最適化、実行のプロセス全体を実行しませんが、クエリキャッシュに直接戻ります。結果セットは
1.2同時実行制御
1.2.1読み取り/書き込みロック
共有ロックおよび排他ロックとも呼ばれます
1.2.2ロックの粒度
共有リソースの同時実行性を向上させる1つの方法は、ロックされたオブジェクトをより選択的にし、すべてのリソースではなく、変更が必要なデータの一部のみをロックしようとすることです。
問題は、ロックやロックの読み取りなどのさまざまなロック操作もリソースを消費することです。
いわゆるロック戦略は、ロックのオーバーヘッドとデータのセキュリティのバランスをとることです。もちろん、このバランスはパフォーマンスにも影響します。
MySQLにはさまざまなオプションがあり、各ストレージエンジンは独自のロック戦略とロックの粒度を実装できます。
テーブルロック
テーブル全体がロックされます。ユーザーが変更、追加、削除などを行う場合、最初に書き込みロックを取得する必要があります。これにより、他のユーザーによるテーブルのすべての読み取りおよび書き込み操作がブロックされます。読み取りロックは互いにブロックしません。
また、書き込みロックは読み取りロックよりも優先度が高いため、書き込みロック要求がキューに挿入される可能性があります。逆に、読み取りロックを書き込みロックキューに挿入することはできません。
行ロック
名前が示すように、データの行をロックすることであるため、テーブルロックのレベルは行のレベルと正確です。
1.3事務
トランザクションは、アトミックSQLクエリのセット、または独立したユニットです。トランザクション内のすべてのステートメントが実行されるか、すべての実行が失敗します。
酸
つまり、原子性、一貫性、分離性、耐久性
アトミシティ:
物事は分割できない最小作業単位と見なす必要があります。つまり、すべてが正常に送信されたか、すべてがロールバックに失敗したかのいずれかであり、操作の一部のみを実行することは不可能です。
一貫性:
データベースは常に1つの整合性状態から別の整合性状態に移行します
隔離:
一般的に言って、トランザクションの最終的な提出の前に行われたことの変更は他の人には見えません
持久性:
トランザクションがコミットされると、加えられた変更はデータベースに永続的に保存されます。
1.3.1分離レベル
SQLには4つの分離レベルがあり、それぞれがトランザクションで行われた変更を指定します。これらはトランザクション内およびトランザクション間で表示され、表示されません。通常、分離レベルが低いほど同時実行性が高くなり、システムのオーバーヘッドも低くなります。
各ストレージエンジンによって実装される分離レベルは同じではありません
読み取り非コミット非コミット読み取り
このレベルでは、トランザクションの変更は、コミットされていなくても、他のトランザクションに表示されます。
トランザクションはコミットされていないデータを読み取ることができます。これはダーティリードとも呼ばれ、特定のセキュリティ問題があります。
パフォーマンスは他のレベルよりもそれほど良くはありませんが、他のレベルのセキュリティが不足しているため、通常はほとんど使用されません。
コミット済みを読む
ほとんどのデータベースシステムのデフォルトの分離レベルはREADCOMMITTEDですが、MySQLはそうではありません。READCOMMITTEDは、前述の分離の単純な定義を満たしています。
トランザクションの開始時には、コミットされたトランザクションによって行われた変更のみを「確認」できます。つまり、トランザクションの開始からコミットされるまで、加えられた変更は他のトランザクションからは見えません。
同じクエリを2回実行すると異なる結果が得られる可能性があるため、このレベルは繰り返し不可の読み取りとも呼ばれます。
再現性のある読み取りの再現性
REPEATABLE READは、ダーティリードの問題を解決します。このレベルにより、同じトランザクションで同じレコードを複数回読み取った結果の一貫性が保証されます。
しかし、理論的には、繰り返し可能な読み取り分離レベルでは、別の
ファントム読み取りの問題を解決することはできません。いわゆるファントム読み取りとは、トランザクションが特定の範囲のレコードを読み取っているときに、別のトランザクションがその範囲に新しいレコードを挿入し、前のトランザクションがその範囲のレコードを再度読み取ると、ファントム行が生成されることを指します。InnoDBおよびXtraDBストレージエンジンは、マルチバージョン同時実行制御(MVCC、マルチバージョン同時実行制御)を介してファントム読み取りの問題を解決します。これについては、この章の後半で詳しく説明します。
繰り返し可能な読み取りは、MySQLのデフォルトのトランザクション分離レベルです。
SERIALIZABLEはシリアル化できます
SERIALIZABLEは最高の分離レベルです。トランザクションを強制的にシリアルに実行することで、前述のファントム読み取りの問題を回避します。簡単に言えば、SERIALIZABLEは読み取られたデータの各行をロックするため、多くのタイムアウトとロック競合の問題が発生する可能性があります。この分離レベルは、実際のアプリケーションではめったに使用されません。データの整合性を確保する必要があり、同時実行性がないことが許容される場合にのみ、このレベルを採用することを検討してください。
分離レベル | ダーティリードの可能性 | 繰り返し不可能な読書の可能性 | ファントムリーディングの可能性 | 読み取りをロック |
---|---|---|---|---|
コミットされていない読み取り | はい | はい | はい | 番号 |
コミット済みを読む | 番号 | はい | はい | 番号 |
繰り返し読む | 番号 | 番号 | はい | 番号 |
シリアル化可能 | 番号 | 番号 | 番号 | はい |
1.3.2デッドロック
デッドロックとは、2つ以上のトランザクションが同じリソースで相互に占有し、相互に占有しているリソースのロックを要求する現象を指します。これにより、悪循環が発生します。複数のトランザクションが異なる順序でリソースをロックしようとすると、デッドロックが発生する可能性があります。複数のトランザクションが同時に同じリソースをロックすると、デッドロックも発生します。
たとえば、次の2つのトランザクションが学生テーブルを同時に処理するとします。
START TRANSACTION
UPDATE Student SET class=5 WHERE student_id = 3 AND DATE="2021-02-12";
UPDATE Student SET class=6 WHERE student_id = 4 AND DATE="2021-02-13";
COMMIT;
START TRANSACTION
UPDATE Student SET student_name="yyyy" WHERE student_id = 4 AND DATE="2021-02-13";
UPDATE Student SET student_name="ssss" WHERE student_id = 3 AND DATE="2021-02-12";
COMMIT;
発生した場合、両方のトランザクションが最初のUPDATEステートメントを実行し、データの行を更新し、データの行をロックしました。次に、各トランザクションが2番目のUPDATEステートメントを実行しようとしましたが、行が相手によってロックされていることがわかりました。 、そして、両方のトランザクションは、もう一方がロックを解放するのを待っていると同時に、もう一方が必要とするロックを保持しているため、無限のループに陥ります。外的要因が介入しない限り、デッドロックを取り除くことが可能です。
この問題を解決するために、データベースシステムはさまざまなデッドロック検出およびデッドロックタイムアウトメカニズムを実装しています。InnoDBストレージエンジンなどのシステムが複雑になるほど、デッドロックの循環依存関係を検出し、すぐにエラーを返すことができます。このソリューションは非常に効果的です。そうしないと、デッドロックによってクエリが非常に遅くなります。別の解決策は、クエリ時間がロック待機タイムアウト設定に達したときにロック要求を放棄することです。この方法は通常、適切ではありません。
デッドロックを処理するInnoDBの現在の方法は、行レベルの排他ロックが最も少ないトランザクションをロールバックすることです(これは比較的単純なデッドロックロールバックアルゴリズムです)。
ロックの動作とシーケンスは、ストレージエンジンに関連しています。同じ順序でステートメントを実行します。一部のストレージエンジンはデッドロックし、一部はデッドロックしません。デッドロックには2つの理由があります。通常は回避が難しい実際のデータの競合が原因である場合もあれば、完全にストレージエンジンの実装が原因である場合もあります。
デッドロックが発生した後、いずれかのトランザクションの部分的または完全なロールバックのみがデッドロックを解除できます。トランザクションシステムの場合、これは避けられないため、アプリケーションは設計時にデッドロックに対処する方法を検討する必要があります。ほとんどの場合、デッドロックのためにロールバックされたトランザクションを再実行するだけで済みます。
1.3.3トランザクションログ
トランザクションログは、トランザクションの効率を向上させるのに役立ちます。
トランザクションログを使用して、ストレージエンジンは、テーブルのデータを変更する際にそのメモリコピーを変更する必要がある、とトランザクション・ログに変更行動を記録し、すべての代わりに、ディスクに変更されたデータ自体を永続化するので、ハードディスク上の持続時間。トランザクションログは追加メソッドを使用するため、ログの書き込み操作は、ディスク上の複数の場所にヘッドを移動する必要があるランダムI / Oとは異なり、ディスク上の小さな領域でのシーケンシャルI / Oであるため、
トランザクションログは次のようになります。使用方法は比較的高速です。
トランザクションログが永続的になった後、メモリ内の変更されたデータをバックグラウンドでディスクにゆっくりとフラッシュバックできます。
現在、ほとんどのストレージエンジンはこのように実装されており、通常はログ先行書き込みと呼ばれます。データを変更するには、ディスクに2回書き込む必要があります。
データの変更がトランザクションログに記録されて永続化されているが、データ自体がディスクに書き戻されていない場合、この時点でシステムがクラッシュし、ストレージエンジンは、再起動時に変更されたデータのこの部分を自動的に復元できます。 。具体的なリカバリ方法は、ストレージエンジンによって異なります。
1.3.4MySQLでのトランザクション
MySQLは、InnoDBとNDBClusterの2つのトランザクションストレージエンジンを提供します。さらに、一部のサードパーティのストレージエンジンもトランザクションをサポートしています。よりよく知られているものには、XtraDBやPBXTなどがあります。それぞれの特性のいくつかについては、後で詳しく説明します。(MyISAMもその中にあります)
自動送信(AUTOCOMMIT)
MySQLはデフォルトでAUTOCOMMITモードを使用します。
つまり、トランザクションを明示的に開始しない場合、各クエリはコミット操作を実行するためのトランザクションとして扱われます。現在の接続では、AUTOCOMIT変数を設定することにより、自動コミットモードを有効または無効にできます。
查看本地默认的提交模式
SHOW VARIABLES LIKE 'AUTOCOMMIT';
ここでの私のオンは、デフォルト値を変更したい場合に、自動送信を有効にすることを意味します
SET AUTOCOMMIT = 0;
AUTOCOMMIT = 0の場合、COMMITコミットまたはROLLBACKロールバックが明示的に実行されるまで、すべてのクエリは1つのトランザクションに含まれ、トランザクションは終了し、同時に別の新しいトランザクションが開始されます。
AUTOCOMMITを変更して
も、MyISAMやメモリテーブルなどの非トランザクションテーブルには影響しません。このタイプのテーブルには、COMMITまたはROLLBACKの概念はなく、AUTOCOMMITが有効なモードになっていることと同等であるとも言えます。
COMMITに実行前に現在のアクティブなトランザクションをコミットするように強制するコマンドもいくつかあります。典型的な例として、データ定義言語(DDL)では、ALTER TABLEのように、大量のデータを変更する操作である場合がこれに該当します。さらに、LOCKTABLESなどの他のステートメントでも同じ結果が生じる可能性があります。必要に応じて、対応するバージョンの公式ドキュメントをチェックして、自動送信を引き起こす可能性のあるすべてのステートメントのリストを確認してください。
MySQLは、SET TRANSACTION ISOLATIONLEVELコマンドを実行することで分離レベルを設定できます。新しい分離レベルは、次のトランザクションの開始時に有効になります。データベース全体の分離レベルは、構成ファイルで設定できます。または、現在のセッションの分離レベルのみを変更できます。
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITED;
MySQLは4つのANSI分離レベルすべてを認識でき、InnoDBエンジンもすべての分離レベルをサポートします。
トランザクションでストレージエンジンを混在させる
MySQLサーバー層はトランザクションを管理せず、トランザクションは基盤となるストレージエンジンによって実装されます。したがって、同じトランザクションで複数のストレージエンジンを使用することは信頼できません。
トランザクション内にトランザクションテーブルと非トランザクションテーブル(InnoDBテーブルやMyISAMテーブルなど)を混在させても、通常のコミットでは問題はありません。
ただし、トランザクションをロールバックする必要がある場合は、非トランザクションテーブルの変更を元に戻すことができないため、データベースが不整合な状態になります。この状況を修復するのは困難であり、トランザクションの最終結果は次のようになります。不確か。したがって、各テーブルに適切なストレージエンジンを選択することが非常に重要です。
非トランザクションテーブルでトランザクション関連の操作を実行する場合、MySQLは通常、リマインダーを発行したり、エラーを報告したりしません。ロールバックされた場合にのみ警告が発行される場合があります。「特定の非トランザクションテーブルでの変更はロールバックできません。」ただし、ほとんどの場合、非トランザクションテーブルに対する操作のプロンプトは表示されません。
暗黙的および明示的なロック
-
暗黙のロック
InnoDBは、2フェーズロックプロトコルを使用します。トランザクションの実行中はいつでもロックを実行できます。ロックはCOMMITまたはROLLBACKが実行されたときにのみ解放され、すべてのロックが同時に解放されます。上記のロックはすべて暗黙的なロックであり、InnoDBは分離レベルに応じて必要に応じて自動的にロックします。
-
ディスプレイロック
さらに、InnoDBは、SQL仕様の一部ではない特定のステートメントによる明示的なロックもサポートしています。
SELECT ... LOCK IN SHARE MODE SELECT ... FOR UPDATE
MySQLはまた、サポートのLOCK TABLESとUNL0CKのTABLESサーバー層で実装され、ストレージエンジンとは何の関係もないされている文を、。それらには独自の用途がありますが、トランザクション処理の代わりにはなりません。アプリケーションでトランザクションを使用する必要がある場合でも、トランザクションストレージエンジンを選択する必要があります。
アプリケーションがテーブルをMyISAMからInnoDBに変換したにもかかわらず、明示的にLOCKTABLESステートメントを使用していることがよくあります。これは不要であるだけでなく、パフォーマンスにも深刻な影響を及ぼします。実際、InnoDBの行レベルのロックの方がうまく機能します。
L0CK TABLESとトランザクションが相互に影響を与えると、状況は非常に複雑になり、一部のMySQLバージョンでは予測できない結果が生成されます。したがって、この本では、トランザクションでAUTOCOMITが無効になっていて、LOCK TABLESを使用できる場合を除いて、使用するストレージエンジンに関係なく、いつでも明示的にLOCKTABLESを実行しないことをお勧めします。
1.4マルチバージョン同時実行制御
MySQLのトランザクションストレージエンジンのほとんどは、単純な行レベルのロックを実装していません。
同時実行パフォーマンスの向上を考慮して、通常、マルチバージョン同時実行制御(MVCC)を同時に実装します。MySQLだけでなく、OracleやPostgreSQLなどの他のデータベースシステムもMVCCを実装していますが、MVCCには統一された実装標準がないため、それらの実装メカニズムは同じではありません。
MVCCは行レベルのロックの変形と見なすことができますが、多くの場合、ロック操作を回避するため、オーバーヘッドが低くなります。実装メカニズムは異なりますが、それらのほとんどは非ブロッキング読み取り操作を実装し、書き込み操作は必要な行のみをロックします。
MVCCの実現は、特定の時点でのデータのスナップショットを保存することによって実現されます。つまり、実行にどれだけ時間がかかっても、各トランザクションで表示されるデータは一貫しています。
トランザクションの開始時刻によっては、同じテーブルの各トランザクションが同時に表示するデータが異なる場合があります。以前にそのような概念がない場合、この文は少し混乱しているように聞こえます。あなたがそれに精通した後、あなたはこの文が実際に非常に理解しやすいことに気付くでしょう。
前述のように、ストレージエンジンごとにMVCCの実装は異なります。典型的なものは、楽観的同時実行制御と悲観的同時実行制御です。以下では、InnoDBの簡略化されたバージョンの動作を使用して、MVCCがどのように機能するかを示します。
InnoDBのMVCCは、レコードの各行の後ろに2つの非表示の列を格納することによって実装されます。これら二つの列のうち、一方が行の作成時刻を保持し、他方は、行の有効期限(または削除時刻)を保持します。
もちろん、保存されるのは実際の時間値ではなく、システムのバージョン番号です。新しいトランザクションを開始するたびに、システムのバージョン番号が自動的にインクリメントされます。
トランザクションの開始時のシステムバージョン番号
は、クエリの各行のバージョン番号と比較されるトランザクションのバージョン番号として使用されます。MVCCがREPEATABLEREAD分離レベルでどのように動作するかを見てみましょう。
SELECT
InnoDB会根据以下两个条件检查每行记录:
a. InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开
始前已经存在的,要么是事务自身插人或者修改过的。
b.行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。只有符合上述两个条件的记录,才能返回作为查询结果。
上記の意味は単純です。前のトランザクションによって変更されたデータのみが検出され、トランザクションの開始前のデータが検出されます。
INSERT
InnoDB为新插入的每一行保存当前系统版本号作为行版本号。
DELETE
InnoDB为删除的每一行保存当前系统版本号作为行删除标识。
UPDATE
InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。
これらの2つの追加のシステムバージョン番号を保存して、ほとんどの読み取り操作のロックを解除できるようにします。この設計により、データの読み取り操作が非常に簡単になり、パフォーマンスが非常に向上します。また、標準を満たす行のみが読み取られることを保証できます。
欠点は、レコードの各行に追加のストレージスペース、追加の行検査作業、および追加のメンテナンス作業が必要になることです。
MVCCは、REPEATABLEREADとREADCOMMITTEDの2つの分離レベルでのみ機能します。他の2つの分離レベルはMVCCと互換性がありません(MVCCには正式な仕様がないため、各ストレージエンジンとデータベースシステムの実装が異なり、他の実装が間違っているとは誰も言えません)
READ UNCOMMITTEDは、現在のトランザクションバージョンに準拠するデータ行ではなく、常に最新のデータ行を読み取るためです。また、SERIALIZABLEは、読み取られたすべての行をロックします。