このドキュメントでは、SelectDB の最適化機能を利用して、オンラインサービングにおいて最大 100,000 QPS、ミリ秒レベルのレイテンシーで高い同時実行性ポイントクエリを実現する方法について説明します。
背景情報
カラム指向ストレージエンジン上に構築された SelectDB は、大規模データセットに対する分析クエリに適しています。しかし、高い同時実行性が求められるオンラインサービングでは、プライマリキーによって行全体を取得する必要が頻繁にあります。この操作はポイントクエリとして知られています。
このようなシナリオでは、従来のカラース指向ストレージエンジンは、主に 3 つの課題に直面します。
I/O 増幅:ワイドテーブルシナリオでは、カラムナストレージから行全体を取得すると、大量のランダム I/O が発生し、クエリパフォーマンスが低下する可能性があります。
過剰な実行オーバーヘッド:単純なポイントクエリの場合、従来の計画および実行パスは長すぎて、大きなオーバーヘッドが発生します。
FE のボトルネック:高い同時実行性の下では、アクセスエントリポイントとして機能する FE (Frontend) が SQL ステートメントの解析と計画を行う際に大量の CPU リソースを消費し、ボトルネックになる可能性があります。
これらの課題に対処するため、SelectDB はクラスター構成、テーブルスキーマ設計、クエリ最適化を網羅したポイントクエリ向けの包括的な最適化ソリューションを提供します。このソリューションは、最大 100,000 の同時リクエストを処理できるオンラインクエリサービスの構築に役立ちます。
クラスターパラメーターの最適化
クラスター構成
高い同時実行性と低レイテンシーのパフォーマンスを実現するには、クラスターパラメーターを最適化する必要があります。事前設定済みテンプレートの使用を推奨しますが、手動でパラメーターを設定することも可能です。
方法 1:構成テンプレートの適用 (推奨)
インスタンスを作成する際、[アプリケーションシナリオ] セクションで 高い並行性シナリオ を選択します。既存のインスタンスの場合は、パラメーターの管理 ページに移動し、高い並行性シナリオ テンプレートを適用します。このテンプレートは、以下の表に記載されている構成を自動的に適用します。
方法 2:パラメーターの手動設定
手動で調整する必要がある場合は、次の表を参照して BE (Backend) のパラメーターを設定してください。
パラメーター | 説明 |
| ベースコンパクションからの出力を優先的にキャッシュし、キャッシュミスによるクエリレイテンシージッターを削減します。 |
| コンパクションの頻度を制御します。ポイントクエリのシナリオでは、このパラメーターを増やすとコンパクションがより積極的になります。これにより、クエリ中にマージされるデータバージョンの数が減り、クエリパフォーマンスが向上します。 |
クラスターグローバル変数
クラスターパラメーターに加えて、クラスターレベルのグローバル変数を調整します。MySQL クライアントで SelectDB に接続し、次の SQL コマンドを実行します。
パラメーター | 説明 |
| デフォルトでは、ポイントクエリは最新のデータバージョンをフェッチするため、メタデータアクセスによるネットワークオーバーヘッドが増加します。この機能を無効 ( |
| ポイントクエリをモニタリングするための監査ログを有効にします。この設定を強く推奨します。有効にしない場合、モニタリングデータ内のレイテンシーと QPS 統計が不正確になります。 |
| 単一クエリのスケジューリングの同時実行数を制御します。単純なポイントクエリの場合、このパラメーターを 1 に設定すると、同時スケジューリングのオーバーヘッドが削減され、通常は最高のパフォーマンスが得られます。これはオプション設定です。 |
テーブル構造の最適化
ポイントクエリのショートサーキット最適化を有効にするには、テーブル作成時に unique key モデルを使用し、行ストレージを有効にする必要があります。
以下は、典型的な高い同時実行性ポイントクエリシナリオにおける CREATE TABLE 文の例です。
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") を有効にすると、追加のストレージ領域を消費します。クエリがカラムのサブセットのみを返す必要がある場合は、CREATE TABLE 文で "row_store_columns" プロパティを使用して、行フォーマットで保存するカラムを指定できます。これにより、ディスク領域を節約できます。例:
PROPERTIES (
...
"row_store_columns" = "k1,v1,v2"
);この場合、ショートサーキット最適化は、`SELECT` リスト内のカラム (例:SELECT k1, v1, v2 FROM ...) が row_store_columns で指定されたカラムのサブセットである場合にのみトリガーされます。
クエリの最適化
PreparedStatement を使用した FE オーバーヘッドの削減
高い同時実行性シナリオでは、FE は SQL ステートメントの解析と式の評価で大量の CPU を消費します。このオーバーヘッドを削減するために、アプリケーションコードで PreparedStatement を使用することを推奨します。
PreparedStatement を使用すると、SQL クエリテンプレートは FE によって事前コンパイルされ、キャッシュされます。後続のクエリはパラメーターを渡すだけでキャッシュにヒットし、解析および計画プロセスのほとんどをバイパスします。FE がボトルネックとなっているシナリオでは、この最適化によってパフォーマンスが 4 倍以上向上する可能性があります。
以下は、JDBC で PreparedStatement を使用する方法の例です。
JDBC URL でサーバーサイドの PreparedStatement を有効にする:
jdbc:mysql://127.0.0.1:9030/ycsb?useServerPrepStmts=trueコード内で PreparedStatement オブジェクトを使用および再利用する:
// PreparedStatement オブジェクトは、クエリごとに作成するのではなく、再利用する必要があります。
PreparedStatement readStatement = conn.prepareStatement("SELECT * FROM tbl_point_query WHERE k1 = ?");
// クエリ 1 を実行
readStatement.setInt(1, 1234);
ResultSet resultSet1 = readStatement.executeQuery();
// クエリ 2 を実行
readStatement.setInt(1, 1235);
ResultSet resultSet2 = readStatement.executeQuery();(オプション) クライアント接続パラメーターをさらに最適化する:
cachePrepStmts=true:クライアントサイドキャッシュを有効にして、重複した prepare リクエストを 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 フィールドは、クエリが PreparedStatement インターフェイスを介して実行され、SQL 解析プロセスを正常にバイパスしたことを示します。
パフォーマンストラブルシューティング
理想的な構成 (例えば、96 コアノードを持つクラスター) では、SelectDB は平均 5 ms 以内のレスポンスレイテンシーで 100,000 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 オーバーヘッドを大幅に削減し、ポイントクエリの同時実行数を数倍に増やすことができます。