このトピックでは、SelectDBで一連の最適化を使用して、オンラインサービス(Serving)シナリオで数万の同時リクエストとミリ秒レベルのレイテンシーを持つ高同時実行性ポイントクエリを実現する方法について説明します。
背景情報
SelectDBは列ストアエンジン上に構築されており、大規模データに対する分析クエリに自然に適しています。しかし、高同時実行性のオンラインサービスシナリオでは、プライマリキーに基づいて行全体を迅速に取得する必要があることがよくあります。これはポイントクエリとも呼ばれます。
このようなシナリオでは、従来の列ストアエンジンは3つの課題に直面します。
I/O増幅:ワイドテーブルシナリオでは、列ストアは行全体を取得する際に大量のランダムI/Oを生成し、クエリパフォーマンスに悪影響を及ぼします。
過剰な実行オーバーヘッド:単純なポイントクエリの場合、従来の実行計画と実行エンジンパスは長すぎ、かなりのオーバーヘッドが発生します。
FEボトルネック:高同時実行性下では、アクセスエントリポイントとして機能するFrontend (FE) は、SQLの解析と計画時に高いCPUオーバーヘッドを生成します。これによりFEがパフォーマンスボトルネックになる可能性があります。
これらの問題に対処するため、SelectDBはクラスター構成、テーブルスキーマ設計、およびクエリ最適化をカバーする完全なポイントクエリ最適化ソリューションを提供します。このソリューションは、数万の同時リクエストを処理できるオンラインクエリサービスを構築するのに役立ちます。
クラスターパラメーターの最適化
クラスター構成
高同時実行性、低レイテンシーのクエリパフォーマンスを実現するには、クラスターパラメーターを最適化する必要があります。プリセット構成テンプレートを使用するか、必要に応じてパラメーターを手動で構成できます。
方法1: 構成テンプレートの適用 (推奨)
インスタンスの作成時に、[アプリケーションシナリオ] から 高い並行性シナリオ を選択します。既存のインスタンスの場合は、パラメーターの管理 ページで 高い並行性シナリオ の構成テンプレートを適用できます。このテンプレートは、以下の表にある構成を自動的に適用します。
方法2: パラメーターの手動構成
パラメーターを手動で調整する場合は、Backend (BE) パラメーター構成について以下の表をご参照ください。
パラメーター | 説明 |
| Base Compaction後の結果データをキャッシュすることを優先し、キャッシュミスによるクエリレイテンシージッターを削減します。 |
| データマージの頻度を制御します。ポイントクエリシナリオでは、このパラメーターを適切に増やすことで、データマージがより積極的になり、クエリ中にマージするデータバージョンの数を減らし、クエリパフォーマンスを向上させます。 |
クラスターグローバル変数
クラスターパラメーターの最適化に加えて、クラスターグローバル変数を調整できます。これを行うには、MySQLクライアントを使用してSelectDBに接続し、以下のSQLコマンドを実行してグローバル変数を調整します。
パラメーター | 説明 |
| ポイントクエリはデフォルトで最新のデータバージョン番号を取得するため、メタデータへのアクセスによるネットワークオーバーヘッドが増加します。この機能を無効にする(`false`に設定する)と、メタデータをキャッシュして再利用することでクエリが高速化されます。注: この機能を無効にすると、データ可視性がわずかに低下します。 |
| ポイントクエリの監査ログの監視を有効にします。強く推奨されます。そうしないと、監視統計の遅延や不正確なQPSにつながります。 |
| 単一クエリのスケジューリングの同時実行性を制御します。単純なポイントクエリの場合、このパラメーターを1に設定すると、同時スケジューリングのオーバーヘッドが削減され、通常は最高のパフォーマンスが達成されます。これはオプション設定です。 |
テーブルスキーマの最適化
ポイントクエリの「ショートサーキット」最適化を有効にするには、テーブルを作成する際にUnique Keyテーブルモデルを使用し、ローストアを有効にする必要があります。
以下は、一般的な高同時実行性ポイントクエリシナリオでのテーブル作成の例です。
CREATE TABLE `tbl_point_query` (
`k1` int(11) NULL,
`v1` decimal(27, 9) NULL,
`v2` varchar(30) NULL,
`v3` varchar(30) NULL,
`v4` date NULL,
`v5` datetime NULL,
`v6` float NULL,
`v7` datev2 NULL
) ENGINE=OLAP
UNIQUE KEY(`k1`)
COMMENT 'OLAP'
DISTRIBUTED BY HASH(`k1`) BUCKETS 1
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"store_row_column" = "true",
"light_schema_change" = "true"
);主要プロパティの説明
UNIQUE KEY(`k1`):テーブルモデルをUnique Keyモデルとして定義し、`k1`をユニークキーとして設定します。"enable_unique_key_merge_on_write" = "true":Unique KeyテーブルモデルのMerge-on-Writeモードを有効にします。これはローストアを有効にするための前提条件です。"store_row_column" = "true":コア最適化。ローストアモードを有効にすると、システムは行指向データの追加コピーを保存します。これにより、ポイントクエリは行全体を直接読み取ることができ、列ストアのI/O増幅の問題を回避し、ショートパス最適化を有効にするための鍵となります。"light_schema_change" = "true":推奨。ショートパス最適化は、この機能によって提供されるカラムユニークIDに依存して、カラムを正確に特定します。
ショートパス最適化のトリガー条件
ショートパスを介して実行されるには、クエリは以下のすべての条件を満たす必要があります。
クエリ文は
SELECT ... FROM ... WHERE ...形式であり、単一テーブルのみをクエリします。WHERE句には、`UNIQUE KEY`のすべてのカラムに対する等価条件(例: `WHERE k1 = 123`)が含まれている必要があり、条件は`AND`で接続されている必要があります。範囲クエリ、`OR`接続、またはその他の複雑な条件はサポートされていません。クエリには、
JOIN、集約、またはネストされたサブクエリを含めることはできません。
コストに関する考慮事項: スペース増幅と部分カラムローストア
ローストア("store_row_column" = "true")を有効にすると、追加ストレージスペースを消費します。クエリが一部のカラムのみを返す必要がある場合は、テーブルを作成する際に"row_store_columns"プロパティを使用して、これらのカラムをローストア用に指定できます。これにより、ディスク領域を節約できます。例:
PROPERTIES (
...
"row_store_columns" = "k1,v1,v2"
);ショートパス最適化は、クエリ内のカラム(例: SELECT k1, v1, v2 FROM ...)がrow_store_columnsのサブセットである場合にのみトリガーされます。
クエリ自体の最適化
PreparedStatement を使用した FE オーバーヘッドの削減
高同時実行性シナリオでは、FEはSQLを繰り返し解析し、式を評価するため、かなりのCPUを消費します。このオーバーヘッドを削減するには、アプリケーションコードでPreparedStatement(プリコンパイル済みステートメント)を使用できます。
PreparedStatementを使用すると、SQLクエリテンプレートはFEによって事前に計算され、キャッシュされます。後続のクエリはパラメーターを渡すだけでキャッシュヒットを達成でき、解析および計画プロセスの大部分をスキップします。FEがボトルネックであるシナリオでは、この最適化により4倍以上のパフォーマンス向上が期待できます。
以下は、JDBCでPreparedStatementを使用する例です。
JDBC URLでサーバーサイドプリコンパイルを有効にします。
jdbc:mysql://127.0.0.1:9030/ycsb?useServerPrepStmts=trueコードでPreparedStatementを使用し、オブジェクトを再利用します。
// The PreparedStatement object should be reused, not created for each query
PreparedStatement readStatement = conn.prepareStatement("SELECT * FROM tbl_point_query WHERE k1 = ?");
// Execute query 1
readStatement.setInt(1, 1234);
ResultSet resultSet1 = readStatement.executeQuery();
// Execute query 2
readStatement.setInt(1, 1235);
ResultSet resultSet2 = readStatement.executeQuery();(オプション) クライアント接続パラメーターをさらに最適化します。
cachePrepStmts=true:クライアントサイドキャッシュを有効にして、FEへのプリコンパイルリクエストの繰り返し送信を回避します。prepStmtCacheSize=250:クライアントがキャッシュできるクエリテンプレートの数を設定します。prepStmtCacheSqlLimit=2048:単一のキャッシュされたSQLテンプレートの最大長を設定します。
検証とトラブルシューティング
ショートパスが有効であることを確認する
EXPLAIN コマンドを使用して、クエリの実行計画を表示し、ショートパス最適化が有効であるかを確認できます。
mysql> EXPLAIN SELECT * FROM tbl_point_query WHERE k1 = 123;
+----------------------------------------------------------+
| Explain String |
+----------------------------------------------------------+
| ... |
| 0:VOlapScanNode |
| TABLE: test.tbl_point_query(tbl_point_query) |
| PREDICATES: `k1` = 123 |
| ... |
| SHORT-CIRCUIT |
+----------------------------------------------------------+実行計画にSHORT-CIRCUITキーワードが含まれている場合、ショートパス最適化が正常に機能していることを示します。
PreparedStatement の有効性を検証する
enable_prepared_stmt_audit_logを有効にした後、FEの監査ログ(`fe.audit.log`)を確認できます。ログに以下に示すように`Stmt=EXECUTE`フィールドが含まれている場合、PreparedStatementがアクティブであることを示します。
... |State=EOF| ... |Time(ms)=2| ... |Stmt=EXECUTE ...ログ内のStmt=EXECUTEは、クエリがプリコンパイル済みインターフェイスを介して実行され、SQL解析プロセスを正常にスキップしたことを示します。
パフォーマンス問題のトラブルシューティングガイド
理想的な構成(例: 96コアクラスター)では、SelectDBは平均応答時間(RT)が5 ms未満で、10,000 Queries Per Second (QPS) を達成できます。ストレステストパフォーマンスが期待を満たさない場合は、以下の方法で問題のトラブルシューティングを行うことができます。
すべての最適化が有効かどうかを確認します:このトピックで言及されているクラスターパラメーター、テーブルスキーマ、およびクエリ最適化が正しく構成され、有効であることを確認します。
クライアントサイドのボトルネックをトラブルシューティングします:ストレステストマシン上のCPU、メモリ、ネットワーク、またはその他のリソースが制限に達しているかどうかを確認します。ストレステストの同時実行性を増やし、QPSがそれに応じて増加するかどうかを観察できます。
SelectDBクラスターのボトルネックをトラブルシューティングします:FEおよびBEノードのCPU使用率が高すぎるか、最大に達しているかどうかを観察します。BEのキャッシュヒット率が100%に近いかどうかを確認します。
よくある質問
Q: 非プライマリキーのクエリはショートパス最適化をトリガーできますか?
A: いいえ、できません。ショートパス最適化では、クエリ条件がUNIQUE KEYのすべてのカラムに対する等価クエリである必要があります。非プライマリキーのクエリは通常のクエリパスに戻ります。
Q: PreparedStatement は非ポイントクエリに有効ですか?
A: PreparedStatement のパフォーマンス最適化は、主にポイントクエリシナリオを対象としています。その他の複雑なクエリの場合、プロトコルレベルでは互換性がありますが、パフォーマンス向上はそれほど大きくありません。
Q: FEのCPUがボトルネックになった場合はどうなりますか?
A: アプリケーションでPreparedStatementを使用する必要があります。これは、FEパフォーマンスボトルネックを解決するための最も効果的な方法です。これにより、FEのCPUオーバーヘッドが大幅に削減され、ポイントクエリの同時実行性を数倍に増やすことができます。