目次
MySQL アーキテクチャ
デフォルトでは、各クライアント接続にはサーバー プロセスにスレッドがあり、接続のクエリはコアまたは CPU に存在するこの単一のスレッドでのみ実行され、サーバーはバッファを保持し、準備ができたスレッドを格納するために使用されるため、新しい接続ごとにスレッドを作成または破棄する必要はありません。
MySQL のロック
MySQL ロックの粒度:
-
行ロック: 高い同時実行性と高いシステム オーバーヘッド (例: カーネル モードとユーザー モード間の頻繁な変換)
-
テーブル ロック: 同時実行数が少なく、システム オーバーヘッドが少ない。
MySQL 読み書きロック:
-
読み取りロック: 読み取りロックは共有ロックとも呼ばれます: 共有リソースに同時にアクセスするときにブロックされません. MySQL のクエリ ステートメントは読み取りロックです. クエリ ステートメントの後に for share / for update を追加すると、クエリ ステートメントに書き込みロックを追加できます.
-
書き込みロック: 排他ロックとも呼ばれ、共有リソースに同時にアクセスするとブロックされます.MySQL では、追加、削除、および変更ステートメントはすべて独自の書き込みロックを持っています.
注: 追加、削除、および変更は暗黙的にロックされます。ロックするには、クエリ ステートメントを明示的にロックする必要があります。
デッドロック:
デッドロックとは、2 つ以上のトランザクションが同じリソースに対してロックを保持し、要求することで、循環依存関係が発生することです。
INnoDB は現在、行レベルの排他ロックが最も少ないトランザクションをロールバックすることで、デッドロックを処理します。実際、InnoDB ストレージ エンジンは、最初にロック状況を検出するのに役立ちます。デッドロックが検出されると、すぐにエラー メッセージが返されます。
デッドロックが発生すると、トランザクションの 1 つをロールバックしない限り、デッドロックを解除することはできません。
MySQL でのトランザクション
取引の特徴
MySQL トランザクション: (INnoDB エンジンのみが MySQL でのトランザクションをサポートします)
トランザクションは、作業単位としてアトミックに処理される SQL ステートメントのグループです。トランザクション内のステートメントのグループとして、すべてのステートメントが正常に実行されるか、すべての実行に失敗します。
トランザクションの 4 つの特徴:
-
原子性: トランザクションは分割できない作業単位と見なす必要があり、トランザクション全体のすべての操作は、正常にコミットされるか、失敗時にロールバックされます。
-
一貫性: 単純に保存と理解することができ、データベースは常に 1 つの一貫した状態から次の一貫した状態に移行します。たとえば、2 つの口座を譲渡した場合、譲渡前と譲渡後の 2 つの口座の合計金額は同じです。
-
分離: 分離の最も一般的なケースの 1 つは、トランザクションによって行われた変更が、最終的にコミットされるまで他のトランザクションから見えないことです。
-
永続性: トランザクションが正常にコミットされると、トランザクションによって行われた変更がデータベースに永続的に保存されます。
分離レベル
MySQL のデフォルトの分離レベルは反復可能読み取りです。
-
READ UNCOMMITTED (READUNCOMMITTED): トランザクションでは、他のトランザクションでコミットされていない変更を表示できます。通常、このレベルの分離はめったに使用されません。
-
READ COMMITTED (READ COMMITTED): トランザクションは、開始後に他のトランザクションによってコミットされた変更を確認できます。トランザクションがコミットされる前に行われた変更は、他のトランザクションには表示されません。(繰り返し不可能な読み取りと繰り返し不可能な読み取りは、このレベルでも発生します。同じトランザクションで同じ SQL ステートメントを 2 回実行すると、異なるデータ結果が表示される場合があります。)
-
反復可能読み取り (REPEATABLE READ): 読み取りコミット分離レベルの非反復可能読み取りの問題を解決し、同じトランザクションで同じ行データを複数回読み取った結果が同じであることを保証します。しかし、それは幻読の問題を解決することはできません。(ファントム読み取り: あるトランザクションが一定範囲のレコードを読み取り、別のトランザクションがその範囲に新しいレコードを挿入し、前のトランザクションが一定範囲のレコードを再度読み取ると、ファントムが生成されることを指します。)
-
シリアライズ可能 (SERIALIZABLE): 以前のトランザクションが順次実行されます。これは、ロックとブロックに相当するため、タイムアウトやロックの競合に関連する多くの問題が発生する可能性があります。この分離レベルは、実際の運用環境ではほとんど使用されません。
分離レベル | ダーティリードがあるかどうか | 非反復可能読み取りが発生するかどうか | ファントムリーディングはありますか |
---|---|---|---|
コミットされていない読み取り | はい | はい | はい |
コミットされた読み取り | いいえ | はい | はい |
繰り返し読み取り | いいえ | いいえ | はい |
シリアライズ可能 | いいえ | いいえ | いいえ |
トランザクション ログ
トランザクション ログは、トランザクションの効率を向上させることができます.ストレージ エンジンは、毎回ディスク上のテーブルを変更する代わりに、メモリ内のデータ コピーを変更するだけで済み、変更されたレコードをトランザクションに書き込みます. トランザクション ログは、ディスク。
トランザクション ログは、ランダム IOではなく、ハードディスクの小さな領域でのシーケンシャル IO である追加の書き込み操作を使用するため、トランザクション ログへの書き込みは比較的高速な操作であり、最終的にバックグラウンド プロセスが実行されます。ディスク上のテーブルを更新する特定の時間。(操作ログを1回順次ディスクに書き込み、その後ディスク内のテーブルを1回更新し、ディスクを2回操作します)
マルチバージョン同時実行制御 MVCC
マルチバージョン同時実行制御 (MVCC): 行レベル ロックの変形として理解できますが、多くの場合、mvcc はロック操作を回避するため、オーバーヘッドが低くなります。
mvcc のアイデアといくつかの設計を簡単に理解します。MVCC の動作原理は、特定の時間ノードでのデータのスナップショットを使用することによって実現されます。
これは、トランザクションの実行時間に関係なく、データの一貫したビューを表示できることを意味します。また、異なるトランザクションが同じテーブル内の異なるデータを同時に表示できることも意味します。
MySQL は、MVCC によるファントム読み取りの問題を解決します。
各ストレージ エンジンは、異なる方法で MVCC を実装します. InnoDB は、開始時に [トランザクション ID] を各トランザクションに割り当てることにより、MVCC を実装します. この ID は、トランザクションが初めてデータを読み取るときに割り当てられます (読み取り専用のトランザクション ID は常には 0) であり、その後のデータの各操作はこのトランザクション ID の値に影響するため、次に読み取るときに、ループを介してこのトランザクション ID を比較して、対応するビューを返すことができます (ビューの存在は異なる時間データを保存するリンクされたリスト)。
注: MVCC は、[反復読み取り] および [コミット済み読み取り] トランザクションの分離レベルにのみ適用されます。
InnoDB はデフォルトで反復可能な読み取り分離レベルに設定され、ギャップ ロック戦略を使用してこの分離レベルでのファントム読み取りを防止します: InnoDB はクエリに含まれる行をロックするだけでなく、インデックス構造のギャップもロックしてファントム読み取りを防止します。挿入されます。
MySQL のパフォーマンスに影響を与える物理的要因
MySQL サーバーのパフォーマンスは、システム全体で最も弱いリンクによって制限されます。MySQL サーバーをホストするオペレーティング システムとハードウェアは、最も重要な制限要因です。最も一般的なものは、ディスク容量、使用可能なメモリ、CPU、ネットワーク、およびディスクの素材です (これには IO が関係するため、ソリッド ステート ドライブを使用できる場合は、ソリッド ステート ドライブを使用してみてください)。
MySQL サーバーに大容量のメモリを構成することは、大量のデータをメモリに保存するためではなく、ディスク IO の数を減らすためです. データへのディスク IO アクセスは、メモリ内のデータへの直接アクセスよりも数桁遅くなります.
MySQL の読み取り、書き込み、およびキャッシュ: 十分なメモリがある場合、ディスク IO の数を大幅に削減できます。データをメモリにロードできれば、サーバーがデータ キャッシュのウォームアップを完了すると、各データread is a cache hit. この場合、論理読み取りは引き続きメモリから実行されますが、データは disk から物理的に読み取られません。また、書き込みはメモリに行うこともできますが、最終的にはデータを永続化するためにディスクに書き込む必要があります. つまり、キャッシュは書き込み操作を遅らせることができますが、キャッシュは書き込み操作のためにディスクを排除することはできません.読み取り操作 IO。
実際、キャッシュの存在により、遅延書き込みに加えて、他の手段と組み合わせて書き込みを使用できます。
-
複数の書き込み操作と 1 回の更新 (この設計は IO の数を減らすだけでなく、ディスクに書き込まれたランダム IO をシーケンシャル IO に変えることもできます)
-
毎回新しい値をディスクに書き込むことなく、データの一部をメモリ内で複数回変更できます。データが最終的にディスクにフラッシュされると、最後の物理書き込み以降に発生したすべての変更が保持されます。
-
-
IO マージ
-
多くの異なるデータをメモリ内で変更でき、これらの変更をまとめて収集できるため、物理的な書き込みを単一のディスク操作として実行できます。
-
書き込み操作はキャッシュの恩恵を受けることができ、ランダム IO をシーケンシャル IO に変えることができます。
メモリとスワップ:
スワッピングは、仮想メモリを保持するのに十分なメモリがないために、オペレーティング システムが仮想メモリをディスクに書き込むときに発生します。書き込み操作はディスクの全体的な寿命を縮めます. もちろん、スワップをオフにすることもできます.これにより、スワップの悪影響を完全に排除できますが、メモリの枯渇によりプロセスが終了する状況に耐える必要があります.
InnoDB バッファ プール
InnoDB バッファー プールは、インデックスをキャッシュするだけでなく、行データ、アダプティブ ハッシュ インデックス、変更バッファー、ロック、およびその他の内部構造などもキャッシュします。 InnoDB はバッファー プールを使用して遅延書き込み操作を実装することもできるため、複数の書き込み操作を組み合わせることができます。そしてそれらを順番に実行します。InnoDB はバッファ プールに大きく依存しているため、十分なメモリを割り当てる必要があります。
もちろん、バッファが大きいと、シャットダウン時間やウォームアップ時間が長くなるなど、他の問題も発生します。また、バッファ プールにダーティ ページ (ディスクに同期されていない変更されたデータ) が多数ある場合は、メモリ内のデータがディスク上のデータと矛盾している場合)、InnoDB は、シャットダウン時にダーティ ページをデータ ファイルに書き込むため、シャットダウンに時間がかかる場合があります。
デフォルトでは、InnoDB は同じバックグラウンド スレッドを使用してダーティ ページを更新し、書き込み操作をマージして順次実行して効率を向上させます. ダーティ ページの割合が設定されたしきい値を超えると、InnoDB は可能な限り迅速にページを更新してページの数を減らします。汚れたページ。
トランザクション ログ: InnoDB uses logs to reduce the cost of committing transactions. It does not flush the buffer pool to disk when each transaction is committed , but record transactions into log (追加メソッドを使用してこのログを記録し、ランダム IO を回避します),このトランザクション ログを使用して、InnoDB はランダム ディスクをシーケンシャル IO に変換できます。InnoDB は、ログのサイズが固定されているため、最終的に変更されたデータをデータ ファイルに書き込む必要があり、循環書き込み方式を採用しています。ログ レコード変更を含み、データ ファイルにまだ適用されていない操作は、トランザクションをコミットするための唯一の永続的なレコードを削除するため、ログ レコードの上書きに失敗します。
トランザクション ログは、継続的なディスク領域を使用して記録されます. InnoDB エンジンでは、トランザクションがコミットされると、トランザクションのすべてのトランザクション ログは、最初に永続化のためにディスク上の redoLog ファイルと undoLog ファイルに書き込まれる必要があります。
ログ バッファ: InnoDB がデータを変更すると、変更レコードがログ バッファに書き込まれ、メモリに保存されます.バッファがいっぱいになると、トランザクションがコミットされるか、1 秒に 1 回(これら 3 つの条件が最初に満たされる) 標準), InnoDB はバッファをディスク上のログ ファイルにフラッシュします。InnoDB の通常のデータと比較すると、ログのエントリは非常にコンパクトです。
InnoDB がログ バッファをフラッシュする方法:
ミューテックスを使用してバッファをロックし、目的の位置にフラッシュし、残りのエントリをバッファの先頭に移動する方法、ミューテックスが解放されると、複数のトランザクションがログエントリをフラッシュする準備ができている可能性があり、InnoDB は A セットを使用します一連のログを 1 回の IO 操作で送信できる送信機能が提供されます。コミットされたトランザクションが完全に持続するようにするには、ログ バッファーを永続ストレージにフラッシュする必要があります。
InnoDB_flush_log_at_trx_commitを使用して、ログ バッファーのフラッシュ位置と更新頻度を制御できます。
-
0:ログ バッファをログ ファイルに書き込み、ログ ファイルを毎秒更新しますが、トランザクションがコミットされたときは何もしません。
-
1:トランザクションがコミットされるたびに、ログ バッファをログ ファイルに書き込み、それを永続ストレージにフラッシュします。これが既定の設定です (そして最も安全な設定です)。
-
2: ログ バッファは、トランザクションがコミットされるたびにログ ファイルに書き込まれますが、更新は実行されません。InnoDB はスケジュールに従って毎秒フラッシュします。設定 0 との主な違いは、MySQL プロセスのみがクラッシュした場合、設定 2 はトランザクションを失うことはありませんが、サーバー全体がクラッシュしたり電源が失われた場合でもトランザクションが失われる可能性があることです。
注: ほとんどのオペレーティング システムでは、バッファをログに書き込むと、データがInnoDB のインメモリ バッファからオペレーティング システムのキャッシュに移動されるだけであり、メモリ内にある間は実際にデータが永続ストレージに書き込まれるわけではありません。クラッシュや停電の場合、データはオペレーティング システムのキャッシュにしか存在しない可能性があるため、0 と 2 の設定では通常、最大で 1 秒のデータ損失が発生します。
MySQL で一般的に使用されるデータ型と最適化
文字列型
変数文字列 varchar:
-
varchar は可変長文字列を格納するために使用され、必要なスペースのみを使用するため、固定長タイプよりもスペース効率が高くなります。
Varchar needs to use 1 or 2 additional bytes to record the length of the string. 列の最大長が 255 バイト以下の場合は、それを表すために 1 バイトのみが使用され、それ以外の場合は、2 バイトが使用されますそれ。たとえば、varchar(10) には 11 バイトのストレージ スペースが必要ですが、varchar(1000) には 1002 バイトのストレージ スペースが必要です。
注: 行は可変長であるため、データを更新するときに増加する可能性があり、追加の作業が発生する可能性があります. 行の増加により、元の場所がそれ以上のコンテンツを収容できなくなる場合、特定の処理動作は使用されるストレージ エンジンによって異なります. たとえば、InnoDB は行を収容するためにページを分割する必要がある場合があります。
次の状況では、varchar を使用することをお勧めします。
-
文字列の最大長は、平均の長さよりもはるかに長くなります。
-
メモリの断片化を避けるために、列が更新されることはめったにありません。
固定文字列:
-
char は固定長です。MySQL は常に、定義された文字列の長さに十分なスペースを割り当てます。char値を格納するとき、MySQL は末尾のスペースをすべて削除します。
char は、非常に短い文字列を格納する場合、またはすべての値がほぼ同じ長さの場合に適しています。固定長はメモリの断片化を起こしにくいです。
日時型
DATETIME: このタイプは、1 マイクロ秒の精度で 1000 年から 9999 年までの広範囲の値を格納できます。タイムゾーンに関係なく、日付と時刻を YYYYMMDDHHMMSS 形式の整数にパックして格納します。ただし、ストレージには8 バイトかかります。
TIMESTAMP (タイムスタンプ): 1970 年 1 月 1 日の午前 0 時 GMT から経過した秒数を格納します。必要なのは4 バイトだけなので、範囲は datatime よりもはるかに小さく、1970 年から 2038 年 1 月 19 日までしか表すことができません。タイムスタンプはタイムゾーンによって異なります。
データ識別子
データ識別子: 一般に、識別子はデータ行の一意の識別子です。たとえば、最も一般的な ID は最も一般的な識別子です。識別子は、主キーの一部またはすべてである場合があります。
識別子のデータ型を選択したら、関連するすべてのテーブルで同じデータ型が使用されていることを確認してください。そうしないと、複数テーブルの結合クエリを実行するときに問題が発生する可能性があります (識別子は、結合テーブルの対応する列と同じである必要があります)データ型は同じままです)。
-
整数型: 通常、整数は高速で自動インクリメントされるため、識別子として最適です。AUTO_INCREMENT は、新しい行の整数値を自動的に生成する列属性です。ただし、このタイプの識別子には欠点もあります。整数型には予期せず整数が不足し、サーバーのダウンタイムが発生する可能性があるため、整数型のデータをビット識別子として選択する場合は、ビット識別子に適した整数サイズを選択するようにしてください。予想されるデータの増加。
-
文字列型: 識別子データ型としての文字列型は、可能な限り避ける必要があります。文字列型は非常にスペースを消費し、一般に整数型よりも遅く、特にインデックスが作成されている場合は遅くなります。
また、MD5()、SHA1()、または UUID() によって生成されたものなど、完全にランダムな文字列には特に注意してください。これらの関数によって生成された新しい値は、大きなスペースに任意に分散され、速度が低下します。挿入の速度と特定の種類の選択クエリ。
-
挿入された値がインデックス内のランダムな場所に書き込まれるため、INSERT クエリの速度が低下します。これにより、ページ分割、ランダムなディスク アクセス、クラスター化されたストレージ エンジンのクラスター化されたインデックスの断片化が発生する可能性があります。
-
論理的に隣接する行 (メモリ内データを参照) がディスクとメモリ全体に広く分散しているため、選択クエリも遅くなります。
-
すべてのタイプのクエリで、ランダムな値はキャッシュのパフォーマンスを低下させる可能性があります。これは、キャッシュの仕組みである参照の局所性が損なわれるためです。
MySQL は null のインデックスを作成しますが、oracle はインデックスを作成しません。