TDSQL-C for MySQLのサーバーレスサービスは弾力性ジッタ防止機能を備えており、様々な作業負荷や性能要件に柔軟に対応できます。本ドキュメントでは、TDSQL-C for MySQLのサーバーレス形態クラスタが持つ弾力性ジッタ防止機能についてご紹介します。
背景
TDSQL-C for MySQLのInnoDBストレージエンジンにおいて、Buffer Poolはデータとインデックスをキャッシュするための重要なメモリ領域です。データをクエリする際、まずBuffer Poolで検索します。データがBuffer Poolにある場合(キャッシュヒット)、InnoDBは即座に結果を返すことができ、ディスクI/Oのオーバーヘッドを回避します。したがって、データベースパフォーマンス向上のため、Buffer Poolサイズの適切な設定が特に重要です。Serverlessアーキテクチャは購入したインスタンス仕様を分離し、ユーザーデータベースの実際の負荷に基づいて自動起動/停止と自動スケーリングを実現し、コンピューティングリソースを極限まで弾力化します。コンピューティングリソースは主にCCU(CPU + メモリ)で構成され、CPUはcgroupやdocker技術で制限可能です。メモリはデータベースプロセスに割り当てられ、大部分がユーザーデータをキャッシュするBuffer Poolモジュールに使用されます。Buffer Poolメモリの割当て・解放プロセスには、ユーザーデータの分散・移行、カーネル内グローバルリソースの排他制御などが関わります。Buffer Poolサイズを適切に設定することで、TDSQL-C for MySQL Serverlessサービスはより安定した弾力的なサービス能力を提供できます。
MySQL公式ではBuffer Poolサイズの動的設定をサポートしており、パラメータinnodb_buffer_pool_sizeを直接調整することで実現します。タスクはバックグラウンドで完了しますが、タスク完了前に再度パラメータを変更した場合、変更は無視されます。拡張ロジックは比較的単純ですが、縮小ロジックはより複雑で、ボトルネックが発生しやすい箇所となります。例えば、I/Oボトルネック、free/lru listミューテックスのボトルネック、グローバルロックのボトルネックなどです。これにより、TDSQL-C for MySQL Serverlessサービスはカーネルレベルで一連の最適化を実施し、データベースがより安定した弾力性を発揮できるようにしています。
ボトルネック分析および最適化ソリューション
I/Oボトルネック
カーネルチームは公式MySQL 8.0のテスト過程において、縮小運用の主要なボトルネックがlruリストのフラッシュにあることを確認しました。ほとんどのシナリオでは、最初のフリーリストスキャンで回収要件を満たせず、回収が必要なブロック数に基づいてスキャンデプスを決定するため、大きな値になる可能性があるためです。buf_flush_do_batchはダーティページのフラッシュ(ページの永続化)を必要とし、この過程でlruミューテックスの頻繁な取得・解放が発生し、ユーザースレッドとの競合によりジッターが生じます。特にページの永続化はI/O操作を伴うため、主要なボトルネックとなります。この問題はTDSQL-C for MySQLのアーキテクチャでは完全に回避可能です。分散ストレージ上のページはすべて、ストレージ層でredo logを適用することで非同期に生成されるため、コンピュートノードはフラッシュ不要であり、淘汰が必要なページは直接破棄できるからです。製品アーキテクチャは下図の通りです。
free/lru listミューテックスのボトルネック
縮小運用の主要なプロセスでは、各ループごとにフリーリストとlruリストを走査します。走査中は対応するミューテックスを保持するため、この間は読み取り操作や書き込み操作を行うユーザースレッドがアクセスできず、フリー/lruリストミューテックスの取得が必要になる場合があります。Buffer Pool内のすべてのブロックはこれら2つのリンクリストで管理されているため、走査プロセスはO(N)(Nはブロック数を示す)の時間計算量を持ちます。この値は非常に大きくなる可能性があり、ミューテックスを長時間保持し続けることでユーザー操作にジッターが発生します。
最適化ソリューション
最適化戦略は、回収対象となるchunk内のblockをアドレス順にスキャンすることです。これによりスキャンするblock数は縮小サイズに依存し、Buffer Pool全体のサイズに依存しません。さらにロック範囲をlruリンクリスト全体から単一blockに縮小します。これによりロック保持範囲と保持時間が削減されます。
グローバルロックボトルネック
拡張操作と縮小操作のいずれにおいても、Buffer Poolのグローバルロックを取得する必要があるロジック段階が存在します。この期間中、Buffer Poolはユーザーに対してほぼ使用不可状態となります。このステップの実行時間が長すぎる場合、ユーザーが一時的な性能低下(「ジッター」)を感知する原因にもなります。分析の結果、このプロセスには主に3つの時間のかかる主要な段階があることが判明しました:
チャンクメモリを回収し、ブロックミューテックスを解放します。
チャンクメモリを割り当て、ブロックを初期化します。
Resize Hash。
最適化ソリューション
最初の2つの段階に対して、カーネルチームはchunksの遅延解放と事前割り当て戦略を採用しました。これにより主要な作業をバッファプールミューテックスの外部で実行可能になります。元々O(N)の複雑さでは、ブロックの初期化と解放によりNがブロック数でしたが、最適化後はO(N)のNがchunks数となり、複雑さが相対的に管理可能になります。Resize Hash段階については、本質的にRehash問題であり、根本的解決策はアルゴリズム最適化(例:ロックフリーハッシュやコンシステントハッシュ割当)です。ハッシュテーブルはInnodbの基本要素であるため、この複雑さを変更するとリスクが高く周期も長くなります。ハッシュテーブルが大きすぎるとスペースが無駄になり(多くのセルが未使用)、小さすぎるとハッシュ衝突が増えて性能に影響します。カーネルチームはスペースと時間のトレードオフ戦略を採用し、トリガー頻度を設定可能にすることで、特定のスケーリング範囲内ではResize Hashを発生させないようにしています。
最適化効果
テストでは sysbench oltp_read_only を使用し、long_query_time = 0.1 を設定して、スロークエリの数を確認します。最適化前後の対比如下。
使用説明
上記のボトルネックと最適化対策として、カーネルチームによる直接的な最適化に加え、Resize Hash段階では関連パラメータ設定を提供しています。これによりユーザーはハッシュテーブルのサイズ変更を実施するかどうかを選択でき、ジッター発生を防止できます。ジッターの主原因はハッシュテーブルのサイズ変更に起因するため、ハッシュテーブルサイズを更新しなければジッターを防止できます。パラメータの詳細説明は以下の通りです。
|
innodb_ncdb_decrease_buffer_pool_hash_factor | はい | いいえ | 2 | 0 | 2 | バッファプールサイズ変更スレッドの頻度を調整すると、ハッシュテーブルのサイズが減少します。 値が2の場合はオフ状態を表し、バッファプールが1/2以上減少した際にハッシュテーブルサイズが更新されることを示します。 値が0の場合は有効状態を示し、ハッシュテーブルサイズが更新されないことを意味します。 説明: このパラメータが有効状態の場合、ノードのコンピューティングリソース上限設定を変更すると、インスタンスが再起動します。 |
パラメータがサポートするカーネルバージョン
カーネルバージョン TXSQL 5.7 2.1.12 以降。
カーネルバージョン TXSQL 8.0 3.1.14 以降。
パラメータ設定方法