全部產品
Search
文件中心

ApsaraDB for SelectDB:高並發點查

更新時間:Apr 21, 2026

本文介紹如何通過一系列最佳化,在 SelectDB 中實現十萬級並發、毫秒級延遲的高並發點查,以滿足線上服務(Serving)情境的需求。

背景

SelectDB 基於列式儲存引擎構建,天然適用於大規模資料的分析查詢。但在高並發的線上服務情境中,通常需要根據主鍵快速擷取整行資料(即點查詢)。

在此類情境下,傳統的列式儲存引擎面臨三大挑戰:

  • I/O 放大:在寬表情境下,列式儲存在擷取整行資料時會產生大量隨機 I/O,影響查詢效能。

  • 執行開銷過重:對於簡單的點查詢,傳統的查詢計劃和執行引擎路徑過長,開銷較大。

  • FE 瓶頸:在高並發下,作為訪問入口的 FE 在解析和規劃 SQL 時會產生較高的 CPU 開銷,可能成為瓶頸。

為解決上述問題,SelectDB 提供了一套完整的點查詢最佳化方案,涵蓋叢集配置、表結構設計和查詢最佳化,可協助您構建萬級並發的線上查詢服務。

最佳化叢集參數

叢集配置

為達到高並發、低延後查詢效能,您需要最佳化叢集參數。建議使用預置的配置模板,也可根據需要手動設定。

方法一:應用配置模板(推薦)

建立執行個體時,在應用情境中選擇高並發點查情境。對於已有執行個體,可在參數管理介面應用高並發點查情境的配置模板。該模板會自動應用下方表格中配置。

方法二:手動設定參數

如果需要手動調整,請參考以下表格調整 BE 參數配置:

參數

說明

enable_file_cache_keep_base_compaction_output = true

將 Base Compaction 後的結果資料優先放入緩衝,以降低因緩衝未命中導致的查詢延遲抖動。

compaction_promotion_version_count = 500

控制資料合併的頻率。在點查情境下,適當調大此參數可使資料合併更積極,從而減少查詢時需要合并的資料版本,提升查詢效能。

叢集全域變數

除了最佳化叢集參數以外,還需要調整叢集全域變數。可以通過 MySQL 用戶端串連 SelectDB ,然後執行以下 SQL 命令調整全域變數:

參數

說明

set global enable_snapshot_point_query = false;

點查預設會擷取最新的資料版本號碼,這會增加訪問中繼資料的網路開銷。建議關閉此特性(設定為 `false`),以緩衝並複用中繼資料來加速查詢。注意:關閉此特性會適當降低資料可見度。

set global enable_prepared_stmt_audit_log = true;

開啟點查詢相關的監控審計日誌。強烈建議開啟,否則會導致監控統計的延遲和 QPS 不準確。

set global parallel_pipeline_task_num = 1;

控制單個查詢的調度並發度。對於簡單的點查詢,將此參數設為 1 可降低並發調度的開銷,效能通常最高。此為可選設定。

最佳化表結構

為啟用針對點查詢的“短路徑(Short-circuit)”最佳化,建表時必須採用 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(Column Unique ID)來精確定位列。

短路徑最佳化的觸發條件

查詢需滿足以下所有條件,才能通過短路徑執行:

  1. 查詢語句為 SELECT ... FROM ... WHERE ... 格式,且只查詢單張表。

  2. WHERE 子句中必須包含對 `UNIQUE KEY` 所有列的等值查詢(例如 `WHERE k1 = 123`),且條件之間為 `AND` 串連。不支援範圍查詢、`OR` 串連或其他複雜條件。

  3. 查詢中不能包含 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 的樣本:

  1. 在 JDBC URL 中開啟服務端先行編譯:

jdbc:mysql://127.0.0.1:9030/ycsb?useServerPrepStmts=true
  1. 在代碼中使用 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();
  1. (可選)進一步最佳化用戶端串連參數:

    • 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 解析過程。

效能問題排查指南

在理想配置下(例如 96C 規格的叢集),SelectDB 可達到 10000 QPS 且平均響應延時在 5ms 以內。如果壓測效能未達預期,請按以下思路排查:

  • 檢查最佳化項是否全部生效:逐一核對本文提到的叢集參數、表結構和查詢最佳化是否已正確配置並生效。

  • 排查用戶端瓶頸:確認壓測機自身的 CPU、記憶體、網路等資源是否已達瓶頸。嘗試增加壓測並發度,觀察 QPS 是否相應提升。

  • 排查 SelectDB 叢集瓶頸:觀察 FE 和 BE 節點的 CPU 使用率是否過高或打滿。檢查 BE 的快取命中率是否接近 100%。

常見問題

Q:非主鍵查詢能否觸發短路徑最佳化?

A:不能。短路徑最佳化嚴格要求查詢條件為對 UNIQUE KEY 所有列的等值查詢。非主鍵查詢會回退到常規的查詢路徑。

Q:PreparedStatement 對非點查查詢有效嗎?

A:目前 PreparedStatement 的效能最佳化主要針對點查情境。對於其他複雜查詢,雖然協議上相容,但效能提升不明顯。

Q:FE 的 CPU 成為瓶頸怎麼辦?

A:請務必在您的應用程式中使用 PreparedStatement。這是解決 FE 效能瓶頸最有效方法,可以大幅降低 FE 的 CPU 開銷,將點查詢並發能力提升數倍。