最初に最初の問題を見てみましょう。SQLステートメントの速度が低下します。
原因分析
SQL文は正常に実行すると非常に高速ですが、何が起こっているのかわからないと非常に遅くなることがあり、そのようなシーンを再現するのは困難です。ランダムであるだけでなく、持続時間が短いです。少し揺れるような。
通常のupdateステートメントは、メモリデータページを更新してREDOログを書き込むディスク書き込みアクションのみを実行しますが、メモリ内のダーティページをディスクに更新する必要があります。つまり、フラッシュアクションです。このフラッシュは、SQLステートメントの操作に影響します。
フラッシュをトリガーするシーンを要約します。
- InnoDBのREDOログがいっぱいです。この時点で、システムはすべての更新操作を停止し、チェックポイントを進め、REDOログが書き込みを続行するためのスペースを残します。チェックポイントの位置を進めるには、2つのポイントと対応するすべてのダーティページの間のログをディスクにフラッシュする必要があります。その後、書き込み位置からチェックポイントまでは、再度書き込むことができるやり直しログの領域です。この問題が発生した時には、システム全体は、もはやすべての更新がブロックされている必要があり、更新を受け入れることはできません。監視から見ると、この時点で更新数は0になります。
- システムメモリが不足しています。新しいメモリページが必要で、メモリが十分でない場合は、一部のデータページを削除し、他のデータページ用にメモリを解放する必要があります。「ダーティページ」を削除する場合は、最初にダーティページをディスクに書き込む必要があります。InnoDBは、バッファプールを使用してメモリを管理します。バッファプール内のメモリページには、未使用、使用済み、クリーンページ、および使用済みページとダーティページの3つの状態があります。InnoDBの戦略は、可能な限りメモリを使用することです。そのため、長時間実行されるライブラリの場合、未使用のページはほとんどありません。読み込むデータページがメモリにない場合は、バッファプールにデータページを申請する必要があります。現時点では、使用頻度の最も低いデータページのみをメモリから削除できます。クリーンなページを削除する場合は、直接解放して再利用します。ただし、ダーティページの場合は、ダーティページをディスクにフラッシュする必要があります。まず、きれいなページになってから再利用できます。
- MySQLがシステムが「アイドル」であると判断すると、いくつかの「ダーティページ」をフラッシュします。
- MySQLが正常にシャットダウンされた場合。MySQLはメモリのすべてのダーティページをディスクにフラッシュするため、次回MySQLを起動したときに、データをディスクから直接読み取ることができ、起動速度は非常に速くなります。
したがって、ダーティページのフラッシュは正常ですが、次の2つの状況がパフォーマンスに大きく影響します。
- ダーティページが多すぎてクエリで削除できないため、クエリの応答時間が大幅に長くなります。
- ログがいっぱいになり、すべての更新がブロックされ、書き込みパフォーマンスが0に低下します。この状況は、機密性の高いビジネスには受け入れられません。
ダーティページをフラッシュするInnoDB制御戦略
パラメータinnodb_io_capacityは、ディスクの容量をInnoDBに通知します。この値をディスクのIOPSに設定することをお勧めします。ディスクのIOPSは、fioツールでテストできます。
fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest
innodb_io_capacityパラメーターが正しく設定されていない場合、設定が小さすぎると、InnoDBはシステムの容量が非常に少ないと見なすため、ダーティページのフラッシュは特に遅く、ダーティページの生成よりもさらに遅くなります。ダーティページ、クエリと更新のパフォーマンスに影響します。これは、MySQLの書き込み速度が非常に遅く、TPSが非常に低いことを示していますが、データベースホストのIOプレッシャーは大きくありません。
ただし、このパラメーターはダーティページをフラッシュする機能を示すだけであり、ユーザー要求を処理する必要もあります。InnoDBのフラッシュ速度は、次の2つの要因に基づいています。1つはダーティページの比率であり、もう1つはREDOログの書き込み速度です。InnoDBはまず、これら2つの要素に基づいて2つの数値を別々に計算します。
- パラメータinnodb_max_dirty_pages_pctは、ダーティページの割合の上限であり、デフォルト値は75%です。InnoDBは、現在のダーティページの比率に基づいて0〜100の範囲の数値を計算します(Mと仮定)。この数値を計算するための擬似コードは、これに似ています。
F1(M) { if M>=innodb_max_dirty_pages_pct then return 100; return 100*M/innodb_max_dirty_pages_pct; }
- InnoDBによって書き込まれる各ログにはシリアル番号があり、現在書き込まれているシリアル番号とチェックポイントに対応するシリアル番号の差はNと見なされます。InnoDBは、このNに基づいて0〜100の範囲の数値を計算します。この計算式は、F2(N)と書くことができます。Nが大きいほど、計算値が大きくなることがわかっている限り、F2(N)アルゴリズムはより複雑です。
- 最後に、上記で計算されたF1(M)とF2(N)の2つの値に従って、大きい方の値をRとします。次に、エンジンはinnodb_io_capacityで定義された容量にR%を掛けて、ダーティページのフラッシュ速度を制御できます。 。
これで、InnoDBがダーティページをバックグラウンドでフラッシュすることがわかりました。ダーティページをフラッシュするプロセスは、メモリページをディスクに書き込むことです。したがって、メモリが必要なときにクエリステートメントでダーティページを削除する必要がある場合でも、ダーティページをフラッシュするロジックが原因である場合でも、クエリステートメントはIOリソースを占有し、更新ステートメントに影響を与え、ビジネスから認識される可能性があります。 sideMySQLへの「シェイク」の理由。つまり、通常はダーティページの比率に注意を払い、75%に近づけないようにします。ダーティページの比率は、Innodb_buffer_pool_pages_dirty / Innodb_buffer_pool_pages_totalから取得されます。特定のコマンドについては、次のコードを参照してください。
mysql> select VARIABLE_VALUE into @a from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty';
select VARIABLE_VALUE into @b from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_total';
select @a/@b;
ただし、MySQLの別のメカニズムにより、クエリが遅くなる可能性があります。ダーティページをフラッシュする準備をしているときに、データページの隣のデータページがダーティページである場合、この「ネイバー」も一緒にフラッシュされます。隣接するデータページごとに、隣接するデータページがまだダーティである場合は、一緒にフラッシュされます。
InnoDBでは、innodb_flush_neighborsパラメーターを使用してこの動作を制御します。値が1の場合、上記の「継続的な座っている」メカニズムがあり、値が0の場合、隣人を見つけて自分でブラッシングしないことを意味します。 。この最適化は、機械式ハードドライブの時代に非常に意味があり、ランダムIOを大幅に削減できます。メカニカルハードディスクのランダムIOPSは、通常、わずか数百です。同じ論理演算でランダムIOを削減することは、システムパフォーマンスが大幅に向上することを意味します。
SSDなどのIOPSが高いデバイスを使用している場合は、innodb_flush_neighborsの値を0に設定することをお勧めします。現時点ではIOPSがボトルネックではないことが多く、「自分でフラッシュするだけ」で必要なフラッシュ操作をより高速に実行し、SQLステートメントの応答時間を短縮できるためです。
上記のダーティページ速度制御に加えて、REDOログを小さく設定することはできません。トランザクションを送信するたびにREDOログを書き込む必要があります。設定が小さすぎると、すぐにいっぱいになり、書き込み位置がCPを追跡しています。このとき、システムはすべての更新を停止し、チェックポイントを進める必要があります。次に、ディスクの負荷は非常に小さいが、データベースのパフォーマンスが断続的に低下していることがわかります。
次に、2番目の質問を見てみましょう。テーブルデータの半分が削除されても、テーブルファイルのサイズは変更されません。
問題分析
InnoDBテーブルには、テーブル構造の定義とデータの2つの部分が含まれています。MySQL 8.0より前は、テーブル構造はサフィックス.frmのファイルに保存されていました。MySQL 8.0バージョンでは、テーブル構造定義をシステムデータテーブルに配置できます。
パラメーターinnodb_file_per_tableは、表データを共有表スペースに保管するか、別のファイルに保管するかを制御できます。
- OFFは、テーブルのデータがシステム共有テーブルスペースに配置されることを意味します。つまり、データディクショナリと一緒に配置されます。
- ONは、各InnoDBテーブルデータが.ibdのサフィックスを持つファイルに保存されることを意味します。
MySQL 5.6.6バージョン以降、デフォルト値はONです。このようなテーブルをファイルとして個別に管理する方が簡単であり、テーブルが不要な場合は、システムがdroptableコマンドを使用してファイルを直接削除します。共有表スペースに配置されている場合、表が削除されても、スペースは再利用されません。
テーブル全体を削除する場合は、droptableコマンドを使用してテーブルスペースを再利用できます。ただし、発生したより多くのデータ削除シナリオは、特定の行を削除することでした。この時点で、記事の冒頭で問題が発生しました。表のデータは削除されましたが、表スペースは再利用されませんでした。
データ削除プロセス
InnoDBのデータはB +ツリー構造で編成されていることがわかっています。レコードを削除したいのですが、InnoDBエンジンはこのレコードを削除済みとしてマークするだけです。後でレコードを挿入したい場合は、この位置を再利用できます。ただし、ディスクファイルのサイズは縮小されません。
したがって、データページのすべてのレコードを削除すると、データページ全体を再利用できます。ただし、データページの多重化はレコードの多重化とは異なります。レコードの多重化は範囲条件を満たすデータに限定され、ページ全体がB +ツリーから削除されると、任意の位置に再利用できるためです。 。
隣接する2つのデータページの使用率が非常に低い場合、システムはこれら2つのページのデータを一方のページにマージし、もう一方のデータページは再利用可能としてマークされます。さらに、deleteコマンドでテーブル全体のデータを削除するとどうなりますか?その結果、すべてのデータページが再利用可能としてマークされます。ただし、ディスク上では、ファイルは小さくなりません。つまり、deleteコマンドを使用してテーブルスペースを再利用することはできません。これらは再利用できますが、未使用のスペースは「穴」のように見えます。
実際、データを削除すると穴が開くだけでなく、データを挿入することにもなります。
データがインデックスの昇順で挿入される場合、インデックスはコンパクトです。ただし、データがランダムに挿入されると、インデックスのデータページが分割される可能性があります。
特定のインデックスの特定のデータページがいっぱいであると仮定して、その範囲にデータの行を挿入したいので、データを保存するために新しいページを申請する必要があります。ページ分割が完了すると、古いページの最後に穴が残り、穴のあるレコードが複数存在する場合があります。
さらに、インデックスの値を更新することは、古い値を削除して新しい値を挿入することと理解できます。これも穴の原因になります。つまり、多数の追加、削除、および変更が行われたテーブルには、穴が存在する可能性があります。したがって、これらの穴を取り除くことができれば、テーブルスペースを縮小するという目的を達成することができます。
テーブルを再構築します
上記の問題分析に基づいて、ボイドを解決すると、スペースを縮小するという目的を達成できます。テーブルを再構築するだけです。
テーブルを再構築するプロセス:
元のテーブルと同じ構造の新しいテーブルを作成し、ソーステーブルから行ごとにデータを読み取り、主キーIDの昇順で新しいテーブルに挿入します。このように、新しいテーブルの古いテーブルの主キーインデックスに穴はありません。明らかに、新しいテーブルの主キーインデックスはよりコンパクトであり、データページの使用率も高くなっています。新しいテーブルを一時テーブルとして使用する場合、データが新しいテーブルにインポートされた後、新しいテーブルが古いテーブルを置き換えます。効果の観点から、古いテーブルスペースが縮小されます。
あなたは使用することができますALTER TABLEのAエンジン=のInnoDBテーブルを再構築するコマンドを。MySQL 5.5以前は、このコマンドの実行プロセスは前に説明したものと同様でした。違いは、この一時テーブルを作成する必要がないことです。MySQLは、データのダンプ、テーブル名の交換、および削除の操作を自動的に完了します。古いテーブル。
このプロセスで最も時間のかかるステップは、一時テーブルにデータを挿入するプロセスです。このプロセス中に古いテーブルに書き込まれる新しいデータがあると、データが失われます。したがって、DDLプロセス全体で、古いテーブルを更新することはできません。つまり、このDDLはオンラインではありません。
MySQL 5.6バージョンで導入されたオンラインDDLは、この操作プロセスを最適化します。
オンラインDDLの導入後、テーブルを再構築するプロセス:
- 元のテーブルの主キーのすべてのデータページをスキャンするための一時ファイルを作成します。
- データページの元のテーブルのレコードを使用してB +ツリーを生成し、一時ファイルに保存します。
- 一時ファイルを生成する過程で、元のテーブルに対するすべての操作をログファイル(行ログ)に記録します。
- 一時ファイルが生成されたら、ログファイルの操作を一時ファイルに適用して、元のテーブルと同じ論理データを持つデータファイルを取得します。
- 表Aのデータファイルを一時ファイルに置き換えます。
通常の状況では、DDLの前にMDL書き込みロックが必要です。alterステートメントは、開始時にMDL書き込みロックを取得する必要がありますが、この書き込みロックは、データが実際にコピーされる前に読み取りロックに縮退します。なぜ退化するのですか?オンラインを実現するために、MDL読み取りロックは追加、削除、および変更操作をブロックしません。ただし、他のスレッドがこのテーブルで同時にDDLを実行するのを防ぐために、直接ロックを解除することはできません。
大きなテーブルの場合、オンラインDDLの最も時間のかかるプロセスは、データを一時テーブルにコピーするプロセスです。このステップの実行中に、追加、削除、および変更を受け入れることができます。したがって、DDLプロセス全体に比べて、ロック時間は非常に短くなります。ビジネスの場合、オンラインと見なすことができます。
上記の再構築方法では、元のテーブルデータがスキャンされ、一時ファイルが作成されることを追加する必要があります。非常に大きなテーブルの場合、この操作はIOおよびCPUリソースを消費します。したがって、オンラインサービスの場合は、稼働時間を慎重に管理する必要があります。より安全な操作が必要な場合は、GitHubのオープンソースのgh-ostを使用することをお勧めします。
オンライン和インプレース
オンラインと言えば、オンラインと、DDLに関連する別の紛らわしい概念との違いを明確にする必要があります。
前述のように、バージョン5.5より前のバージョンでは、テーブルの再構築は一時テーブルにデータを挿入し、バージョン5.6以降は、データを一時ファイルに配置します。前者はサーバーレイヤーで実行され、後者はInnoDBエンジンで実行されます。レイヤー。子。
次に、サーバーレイヤーの場合、データを一時テーブルに移動しないのは「インプレース」操作です。これは「インプレース」名のソースですが、一時ファイルも一時スペースを占有します。
テーブルaltertable t engine = InnoDBを再構築するというステートメントは、実際には、alter table t engine = innodb、ALGORITHM = inplace;を意味します。
インプレースに対応するのは、テーブルをコピーする方法です。テーブルを変更します。tengine = innodb、ALGORITHM = copy;
ALGORITHM = copyを使用すると、テーブルが強制的にコピーされることを意味し、対応するプロセスは一時テーブルの操作プロセスです。
この時点まで、一見インプレースもオンラインですが、実際には、テーブルを再構築するロジックがインプレースであり、DML操作である可能性があるためです。
たとえば、次のように記述されたInnoDBテーブルのフィールドにフルテキストインデックスを追加したいとします。altertablet add FULLTEXT(field_name);このプロセスはインプレースですが、追加、削除、および変更操作をブロックします。そしてそれは非オンラインです。
これら2つのロジック間の関係は、次のように要約できます。
- DDLプロセスがオンラインの場合は、インプレースである必要があります。
- 逆は必ずしも当てはまりません。つまり、インプレースのDDLはオンラインではない可能性があります。MySQL 8.0以降、これは全文索引(FULLTEXT索引)と空間索引(SPATIAL索引)が追加された場合です。
テーブルを拡張、最適化、テーブルの分析、テーブルの変更を行うには、テーブル間の違いを再構築する3つの方法があります。
- MySQL 5.6バージョン以降、alter table t engine = InnoDB(つまり、再作成)は、デフォルトで上記の一時ファイルにデータを格納するプロセスになります。
- 分析テーブルtは実際にはテーブルを再構築していませんが、データを変更せずにテーブルのインデックス情報を再記述しています。このプロセスでMDL読み取りロックが追加されます。
- 最適化テーブルtは、再作成+分析と同じです。
テーブルの再構築に関しては、極端な問題があります。
alter table t engine = InnoDBを使用すると、テーブルが占めるスペースが増えることがあります。
理由:テーブルを再構築するとき、InnoDBはテーブル全体を埋めることはなく、各ページの1/16は後続の更新用に予約されています。つまり、テーブルを再構築した後の「最も」コンパクトではありません。再構築する前に新しいDML操作がある場合、それはページの残りのスペースを占有します。このとき、再度縮小すると1/16までスペースを確保し続けるため、縮小後はファイルが大きくなります。
コンテンツソース:LinXiaobin「MySQLの実際の戦闘に関する45の講義」