本文介绍如何通过一系列优化,在 SelectDB 中实现万级并发、毫秒级延迟的高并发点查,以满足在线服务(Serving)场景的需求。
背景
SelectDB 基于列式存储引擎构建,天然适用于大规模数据的分析查询。但在高并发的在线服务场景中,通常需要根据主键快速获取整行数据(即点查询)。
在此类场景下,传统的列式存储引擎面临三大挑战:
I/O 放大:在宽表场景下,列式存储在获取整行数据时会产生大量随机 I/O,影响查询性能。
执行开销过重:对于简单的点查询,传统的查询计划和执行引擎路径过长,开销较大。
FE 瓶颈:在高并发下,作为访问入口的 FE 在解析和规划 SQL 时会产生较高的 CPU 开销,可能成为瓶颈。
为解决上述问题,SelectDB 提供了一套完整的点查询优化方案,涵盖集群配置、表结构设计和查询优化,可帮助您构建万级并发的在线查询服务。
优化集群参数
集群配置
为达到高并发、低延迟的查询性能,您需要优化集群参数。建议使用预置的配置模板,也可根据需要手动配置。
方法一:应用配置模板(推荐)
创建实例时,在应用场景中选择高并发点查场景。对于已有实例,可在参数管理界面应用高并发点查场景的配置模板。该模板会自动应用下方表格中配置。
方法二:手动配置参数
如果需要手动调整,请参考以下表格调整 BE 参数配置:
参数 | 说明 |
| 将 Base Compaction 后的结果数据优先放入缓存,以降低因缓存未命中导致的查询延迟抖动。 |
| 控制数据合并的频率。在点查场景下,适当调大此参数可使数据合并更积极,从而减少查询时需要合并的数据版本,提升查询性能。 |
集群全局变量
除了优化集群参数以外,还需要调整集群全局变量。可以通过 MySQL 客户端连接 SelectDB ,然后执行以下 SQL 命令调整全局变量:
参数 | 说明 |
| 点查默认会获取最新的数据版本号,这会增加访问元数据的网络开销。建议关闭此特性(设置为 `false`),以缓存并复用元数据来加速查询。注意:关闭此特性会适当降低数据可见性。 |
| 开启点查询相关的监控审计日志。强烈建议开启,否则会导致监控统计的延迟和 QPS 不准确。 |
| 控制单个查询的调度并发度。对于简单的点查询,将此参数设为 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)来精确定位列。
短路径优化的触发条件
查询需满足以下所有条件,才能通过短路径执行:
查询语句为
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并复用对象:
// 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:启用客户端缓存,避免重复向 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 开销,将点查询并发能力提升数倍。