联邦查询 #
当用户使用数据分片对海量数据进行水平拆分时,虽然能够有效解决数据库性能瓶颈,但业务上也因此带来了一些新的问题。
例如以下场景:跨数据节点关联查询、子查询、分页、排序、聚合查询、集合运算 等,这些场景在数据水平分片后,SQL 功能会受到影响,无法完美支持。用户在进行业务实现时需要注意 SQL 的使用范围,尽量避免跨数据节点关联查询、复杂子查询等场景,这使得业务层面的功能受到了数据库的限制。
为了减少用户业务改造工作量,SphereEx-DBPlusEngine 提供了联邦查询功能,通过 SQL 解析、SQL 校验、SQL 优化 和 SQL 执行 等 4 个核心步骤,能够支持水平分片后的跨数据节点关联查询、子查询、分页、排序、聚合查询、集合运算等功能。
概述 #
SphereEx-DBPlusEngine 的联邦查询功能,完整地实现了分布式 SQL 查询能力,功能内部会将 SQL 语句解析为 AST,然后通过 SQL 绑定元数据分析出 SQL 语义,并将绑定后的 AST 转换为逻辑关系代数树(即逻辑执行计划),再通过 RBO 和 CBO 优化,生成最优的物理执行计划,最后再交由联邦查询执行引擎高效执行物理计划,并返回查询结果。
基本概念 #
逻辑执行计划 #
逻辑执行计划是对查询语句的抽象表示,它包含了查询需要执行的操作,例如:投影、选择、连接、聚合等操作。
物理执行计划 #
物理执行计划详细规定了每一步操作将如何被执行,即具体的算法和数据访问路径,例如,对于一个连接操作,物理计划会指定使用哪种连接算法(如:嵌套循环连接 NestedLoopJoin、哈希连接 HashJoin、排序-合并连接 SortMergeJoin 和 查找连接 LookupJoin)。
统计信息 #
统计信息(Statistic)为优化器的代价(Cost)计算提供了必要的数据支撑,通常我们可以将统计信息划分为基础统计信息和高级统计信息。
基础统计信息负责大部分通用场景下的代价计算,具体包括表级别的统计信息 Row Count,单个字段的统计信息:每个字段的 NDV 值(The Number of Distinct Values),Max 值,Min 值,NULL 值,Histogram 值(分布信息,用于区间查询), Count-Min Sketch 值(用于等值查询),DataSize 值等。由于基础统计信息对代价计算至关重要,需要做到自动收集,自动更新,否则很可能因为基础统计信息的缺失,导致优化器产生灾难性的执行计划。
高级统计信息主要用于提升复杂场景下的优化质量,通常包括多字段间的关联度(Column Group)、Functional Deplendency、数据倾斜等,高级统计信息需要手工触发,只有在必要的时候才会收集。
代价模型 #
代价模型(Cost Model)是用于估算物理执行计划的代价,代价通常使用 CPU、Memory、IO、Net 四元组来描述,每一算子都会通过上述四元组来描述其代价。执行计划的代价即是其全部算子的代价之和,最终优化器会根据求和后的 CPU、Memory、IO、Net 加权计算出执行计划的最终代价。
CPU:代表CPU的消耗数值;Memory:代表Memory的占用量;IO:代表磁盘的逻辑IO次数;Net:代表网络的逻辑IO次数(交互次数及传输量);Cost=(CPU, Memory, IO, Net) * (w1, w2, w3, w4),w为权重向量。
磁盘溢出 #
对于内存密集型操作,联邦查询引擎提供了磁盘溢出机制,当内存使用超过设定的阈值时,联邦查询引擎会将部分内存数据溢出到磁盘上进行分批处理,从而避免过多内存消耗导致系统 OOM。开启磁盘溢出时,需要注意计算节点的磁盘空间是否充足,避免因磁盘空间不足导致联邦查询失败。另外,也需要确保操作系统的文件句柄限制,避免文件句柄限制过小无法创建溢出文件。
适用场景 #
联邦查询适用于水平分片场景下,对跨数据节点 JOIN(绑定表不带分片键 JOIN、非绑定表的 JOIN、单表和分片表 JOIN、单表和单表跨数据源 JOIN 等)、子查询(无法路由到单节点的子查询)、聚合查询(包含 Having、聚合查询部分函数包含 DISTINCT、表达式中嵌套聚合函数)、集合运算等场景进行支持。
除了查询语句外,联邦查询引擎还可以支持 INSERT、DELETE、UPDATE 语句中涉及子查询,多表 JOIN(DELETE JOIN、UPDATE JOIN) 以及 ORDER BY LIMIT 排序分页(DELETE ORDER BY LIMIT、UPDATE ORDER BY LIMIT)场景,当使用分片表或者跨数据源的单表遇到这些场景时,联邦查询引擎能够自动识别并进行处理。
使用前提 #
- 服务器中已安装 SphereEx-DBPlusEngine 和数据库集群,且服务运行正常;
- 使用联邦查询功能前,需要先开启数据字典功能,保证统计信息已经正确收集;
- 开启联邦查询磁盘溢出功能,需要保证计算节点部署机器有充足磁盘空间,文件句柄限制设置合理;
- 相关使用限制需提前了解,详见下方
使用限制内容。
使用限制 #
- 联邦查询目前仅支持 MySQL,其他数据库,例如:PostgreSQL、openGauss、Oracle 等,需要适配数据库方言函数和运算符;
- 联邦查询目前支持查询语句,以及 DML 语句中的
INSERT、DELETE和UPDATE,其他 DML 语句待适配; - 联邦查询暂不支持视图、CTE(WITH SELECT 语句)、Window 窗口函数、JSON 函数运算;
- 联邦查询暂不支持 MySQL CONVERT 函数转换为
SIGNED和UNSIGNED类型,例如:SELECT CONVERT('1', UNSIGNED);; - 联邦查询暂不支持 MySQL 投影列中包含不在 GROUP BY 中的列特性(MySQL 通过删除
sql_mode中的ONLY_FULL_GROUP_BY属性开启该特性),例如:SELECT id, name FROM table GROUP BY id;; - 联邦查询暂不支持 MySQL 投影列中使用 DISTINCT 去重,并且 ORDER BY 中使用去重列之外的列进行排序,例如:
SELECT DISTINCT name FROM table ORDER BY id;; - 联邦查询暂不支持查询语句中使用变量以及
SELECT INTO语句,例如:SET @var = 1; SELECT * FROM table WHERE id = @var;,SELECT INTO OUTFILE; - 联邦查询暂不支持查询语句中
FOR UPDATE、LOCK IN SHARE MODE等加锁语句; - 联邦查询暂不支持聚合语句中的
WITH ROLLUP特性,例如:SELECT id, COUNT(*) FROM table GROUP BY id WITH ROLLUP;; - 联邦查询暂时只支持 UTF8 系列编码,其他编码待适配;
- 联邦查询暂时只支持
utf8mb4_general_ci、utf8mb3_general_ci、utf8_general_ci排序规则,其他排序规则待适配; - 联邦查询中使用
UPDATE语句、INSERT INTO ON DUPLICATE KEY UPDATE语句更新分片键时,需要保证新分片键值不改变原有的数据分布(即根据新分片键值计算的路由结果需和原始值一致); - 联邦查询中使用
INSERT IGNORE、INSERT INTO ON DUPLICATE KEY UPDATE、REPLACE语句时,需要注意唯一键和分片键的设置,由于 DBPlusEngine 暂不支持分片场景下的全局唯一键,当分片键和唯一键不同时,可能会导致唯一键相同的记录路由到不同的分片上,从而无法保证唯一键全局唯一; - 联邦查询中使用
DELETE或UPDATE语句时,表中必须包含主键或唯一键,用于高效批量删除或更新; - 联邦查询暂不支持
INSERT INTO ON DUPLICATE KEY UPDATE更新部分包含子查询语句; - 联邦查询暂不支持
INSERT VALUES AS row_alias和INSERT SET AS row_alias语法,例如:INSERT INTO t_order VALUES (1, 'ok') AS new_order(new_id, new_status);; - 联邦查询支持
INSERT语句中的IGNORE关键字,暂不支持其他LOW_PRIORITY、DELAYED、HIGH_PRIORITY关键字,DELETE和UPDATE语句暂不支持LOW_PRIORITY、QUICK、IGNORE关键字; - 联邦查询暂不支持
INSERT、DELETE和UPDATE语句中使用DEFAULT关键字生成字段默认值,例如:INSERT INTO t_order (order_id, status) VALUES (1, DEFAULT);; - 联邦查询暂不支持当前事务中,先写后读(读走内核不影响,读走联邦查询需要多个连接完成,无法利用本地事务的特性),立刻查询到刚写入的数据,例如:
BEGIN; INSERT INTO t_order (order_id, status) VALUES (1, 'ok'); SELECT * FROM t_order WHERE order_id < (SELECT order_id FROM t_order_item LIMIT 1); COMMIT;; - 更多关于联邦查询功能支持和不支持 SQL 示例,请参考 联邦查询 SQL 兼容性。
注意事项 #
- 联邦查询功能由于在计算层实现 SQL 运算,因此会消耗额外的内存,通常建议使用单独部署的 Proxy 接入端执行联邦查询,并根据参与 SQL 运算的数据量调整合适的 JVM 参数和
maxUsageMemoryPerQuery参数。如果想在 JDBC 接入端使用联邦查询,需要充分评估应用程序的可用内存及业务并发量,设置合理的maxUsageMemoryPerQuery参数,并且开启磁盘溢出功能,必要时可能需要调大 JDBC 应用程序的最大内存; - 联邦查询磁盘交换依赖统计信息进行内存估算,因此必须提前开启统计信息收集功能。此外,当业务有大批量数据变更时,需要用户主动刷新统计信息(通过
REFRESH STATISTICS METADATA),保证统计信息的准确性; - 考虑到统计信息可能存在偏差,联邦查询磁盘溢出功能,支持运行时动态内存统计,基于动态统计结果判断是否需要磁盘溢出,避免 SQL 执行内存占用过多。但需要注意,运行时磁盘溢出属于后置兜底逻辑,可能因为执行计划选取不是最优,而性能较差,因此需要优先保证统计信息的准确性和及时性;
- 联邦查询从 MySQL 数据库中读取数据采用的是流式查询方式,由于 MySQL 驱动
netTimeoutForStreamingResults默认值为 600 秒,当 SQL 执行时长超过 600 秒时,MySQL 服务端会主动断开连接,因此建议根据业务 SQL 执行的时长,在存储单元的 URL 属性中适当调大netTimeoutForStreamingResults参数,避免连接被 MySQL 服务端断开; - 使用联邦查询功能需要配置集群模式(依赖统计信息),采用 Proxy + JDBC 混合部署模式,Proxy 端通过定时任务收集统计信息,JDBC 接入端使用注册中心同步的统计信息,从而保证联邦查询能够生成高效准确的执行计划;
- 联邦查询中使用
UPDATE JOIN语句时,如果 SET 的列来自于其他列中的行值,例如:UPDATE t1 JOIN t2 ON t1.id = t2.id SET t1.col1 = t2.col2;,需要特别注意此场景下,虽然联邦查询会使用 JDBC 中的executeBatch接口批量更新,但考虑到更新是基于数据行进行,性能可能会不佳,不推荐在性能敏感的场景中使用该语法。
原理介绍 #
如下图所示,联邦查询引擎处理 SQL 主要包括 4 个核心步骤:SQL 解析、SQL 校验、SQL 优化 和 SQL 执行,下面我们分别来介绍下这 4 个步骤的具体实现原理。

SQL 解析 #
SQL 解析负责将 SQL 字符串转换成为 AST(抽象语法树),方便后续 SQL 校验和生成执行计划。由于这部分的功能使用的是 SphereEx-DBPlusEngine 解析引擎,其内部实现原理请参考 SQL 解析引擎,本文不再重复介绍。
SQL 校验 #
SQL 校验可以分为语法检查和语义分析 2 个步骤,语法检查会根据联邦查询引擎中的元数据信息进行语法验证,例如:检查表是否存在、列是否存在、函数使用是否符合语法规范等,校验后可以得到绑定元数据的 AST 语法树。
语义分析则负责将已绑定的 AST 语法树转换为关系代数树(即:最初版的逻辑执行计划 Logical Plan),通过关系代数树中不同的运算符表示 SQL 语义。
例如:SELECT * FROM t_order o INNER JOIN t_order_item i ON o.order_id = i.order_id WHERE o.order_id = 3; 语句,对应的执行计划如下,LogicalProject 运算符表示 SQL 中的投影,即:SELECT *,LogicalFilter 运算符表示 SQL 中的过滤,即:WHERE o.order_id = 3,LogicalJoin 运算符表示 SQL 中的关联,即:t_order o INNER JOIN t_order_item i ON o.order_id = i.order_id,而 LogicalScan 则表示了表扫描,即:FROM t_order o 和 t_order_item i。

SQL 优化 #
SQL 优化是指基于关系代数的等价替换原则,将原始执行计划进行等价变换改写,从而寻找到最优执行计划的过程。
SQL 优化按照优化的方式,可以分为:基于规则的优化(RBO)、基于代价的优化(CBO),RBO 优化使用预先定义的优化规则,对执行计划进行等价改写,常用的 RBO 优化规则包括:列裁剪、子查询转换为关联查询、投影下推、谓词下推等。CBO 优化则是基于代价选择最优执行计划,每个运算符都提供了代价的计算方法,优化器会根据运算符的代价进行计算,得到整个执行计划的代价,并从多个执行计划中选择出代价最小的执行计划。
需要注意的是,代价的估计是相对的,只需要能够区分出不同执行计划成本的相对大小即可,代价的估算依赖统计信息,统计信息越完善代价估算越准确。
下图展示了经过 RBO 优化后的逻辑执行计划,可以看到 RBO 优化过程中,应用了谓词下推规则,会将 LogicalJoin 之上的 LogicalFilter 下推到 LogicalScan 中,这样在扫描 DB 数据时就可以提前过滤掉不需要的数据。
此外,由于 order_id 列是等值关联条件中的列,优化规则可以分析出 t_order_item 同样可以下推 LogicalFilter 过滤条件。

RBO 优化后会继续进行 CBO 优化,CBO 优化过程中会将逻辑执行计划转换为物理执行计划,物理执行计划详细规定了每一步操作将如何被执行,即具体的算法和数据访问路径。
下图展示了经过 CBO 优化后的物理执行计划,可以看到逻辑执行计划中的 LogicalProject 被转换为 DBPlusEngineCalc,LogicalJoin 被转换为 DBPlusEngineHashJoin,由于 JOIN 运算符在联邦查询中存在多种执行方式,例如:嵌套循环连接、哈希连接、排序-合并连接等,优化器在选择 Join 的执行方式时,会计算每种 Join 运算符的代价,选择执行代价最小的执行计划。
LogicalScan 会被转换为 DBPlusEngineTableScan,并且会将下推的过滤条件和表扫描一起转换为下推 SQL。

SQL 执行 #
SQL 执行负责根据 SQL 优化生成的最优执行计划,进行高效的 SQL 执行。SQL 执行采用了经典的 Volcano 执行模型,在该模型中,每个物理运算符都对应了一个迭代器,迭代器提供了一组简单的访问接口:open -> next -> close。
在联邦查询中,迭代器被封装为执行器对象 DBPlusEngineExecutor,DBPlusEngineExecutorFactory 工厂类负责将物理执行计划转换为执行器树,执行时从最顶层的执行器开始调用,每调用一次 moveNext 接口,就会返回一行数据,数据通过自上而下的 moveNext 嵌套调用而被逐行拉取,当所有数据被拉取完后,整个 SQL 执行就结束了。
操作指南 #
使用 DBPlusEngine 联邦查询功能时,需要按照如下的操作步骤进行。
- 执行 DistSQL
SET DIST VARIABLE dbplusengine_convention_enabled = true;切换到商业版联邦查询功能; - 梳理业务系统的 SQL 并发量,根据 SQL 并发量及 DBPlusEngine 可用内存,评估联邦查询
maxUsageMemoryPerQuery参数的大小,然后配置联邦查询规则。例如:SQL 并发量为500 QPS,当前部署的 DBPlusEngine 可用内存为16G,计划分配4G内存用于联邦查询的内存计算,则可以推算出maxUsageMemoryPerQuery合理的配置为8M,计算方式为:maxUsageMemoryPerQuery = 联邦查询内存计算可用总内存 / SQL 并发量; - 准备充足的磁盘空间用于联邦查询磁盘交换,当单次查询使用的内存超过
maxUsageMemoryPerQuery时会将内存数据交换到磁盘,再分批进行处理。执行 DistSQL 修改联邦查询规则,开启联邦查询功能,并配置maxUsageMemoryPerQuery及磁盘溢出参数; - 执行 DistSQL 修改统计信息存储规则,将统计信息存储到 DB 中,并对统计信息存储表进行赋权;
- 执行 DistSQL
SET DIST VARIABLE proxy_meta_data_collector_cron = '0 0/30 * * * ?';设置字典表收集间隔。生产环境的时间间隔需要考虑数据的变化情况,通常 1 天更新 1 次即可,当通过定时任务、脚本等批量更新时,需要主动执行REFRESH STATISTICS METADATA更新统计信息; - 执行 DistSQL
SET DIST VARIABLE proxy_meta_data_collector_enabled = 'true';开启字典表收集功能; - 检查统计信息是否已经收集,执行
SELECT * FROM shardingsphere.table_statistics;及SELECT * FROM shardingsphere.column_statistics;是否返回结果; - JDBC 接入端通过配置相同的
namespace和databaseName接入联邦查询功能。
配置示例 #
开启联邦查询功能 #
首先,通过 Proxy 执行 DistSQL SET DIST VARIABLE dbplusengine_convention_enabled = true; 切换到商业版联邦查询功能。
然后执行 DistSQL 修改联邦查询规则,SQL_FEDERATION_ENABLED 设置为 true 开启联邦查询功能。ALL_QUERY_USE_SQL_FEDERATION 设置为 false 关闭全部查询走联邦查询标记,内部会根据 DBPlusEngine 内核的支持情况,将不支持的语句路由到联邦查询引擎执行。
MAX_USAGE_MEMORY_PER_QUERY 参数表示每个查询最大可用内存,该参数需要根据业务 SQL 并发量以及联邦查询可用内存进行计算,当开启磁盘交换并且内存不足时,会将内存数据交换到磁盘,通过拆分多个批次来完成 SQL 计算。
SPILL_ENABLED 参数用于开启磁盘交换功能,SPILL_PATH 参数用于指定磁盘交换的本地文件路径,默认为 /tmp/dbplusengine/spill,SPILL_COMPRESSION_ENABLED 参数用于开启磁盘交换文件压缩,开启压缩可以减少磁盘占用,但是压缩会占用更多 CPU 资源。
特别注意:禁止在业务高峰期更新执行计划缓存
EXECUTION_PLAN_CACHE相关参数,修改操作可能会导致执行计划失效,导致 SQL 性能下降。
ALTER SQL_FEDERATION RULE (
SQL_FEDERATION_ENABLED=true,
ALL_QUERY_USE_SQL_FEDERATION=false,
EXECUTION_PLAN_CACHE(INITIAL_CAPACITY=2000, MAXIMUM_SIZE=65535, TTL_MILLI_SECONDS=86400000),
MAX_USAGE_MEMORY_PER_QUERY='1M',
SPILL_ENABLED=true,
SPILL_PATH="file:///tmp/dbplusengine/spill",
SPILL_COMPRESSION_ENABLED=true
);
判断 SQL 走 DBPlusEngine 内核执行,还是走联邦查询引擎执行,可以通过执行 EXPLAIN 语句进行观察,如果 SQL 走 DBPlusEngine 内核执行,EXPLAIN 语句会透传到 DB 执行,返回的执行计划是原生的 DB 执行计划。
如果 SQL 走联邦查询引擎执行,则返回联邦查询引擎的执行计划,如下展示了联邦查询引擎的执行计划。
mysql> EXPLAIN SELECT * FROM t_order o INNER JOIN t_order_item i ON o.order_id = i.order_id WHERE o.user_id = 10 ORDER BY o.order_id, 7;
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| PLAN |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| DBPlusEngineSort(sort0=[$0], sort1=[$6], dir0=[ASC], dir1=[ASC]) |
| DBPlusEngineSortMergeJoin(condition=[=($0, $7)], joinType=[inner]) |
| DBPlusEngineScan(table=[[sphereex_db_tbl_sql_federation, t_order]], sql=[SELECT * FROM `sphereex_db_tbl_sql_federation`.`t_order` WHERE `user_id` = 10 ORDER BY `order_id`]) |
| DBPlusEngineScan(table=[[sphereex_db_tbl_sql_federation, t_order_item]], sql=[SELECT * FROM `sphereex_db_tbl_sql_federation`.`t_order_item` ORDER BY `order_id`]) |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
4 rows in set (0.06 sec)
通过 EXPLAIN 语句可以对联邦查询执行计划进行分析,判断当前的执行计划是否符合预期,EXPLAIN 语句还支持 SQL Hint 语法:/* SPHEREEX_HINT: SHOW_EXPLAIN_COST=true */,通过在 EXPLAIN 前面增加 SQL Hint 可以展示优化器内部估算的详细 Cost 信息。
需要注意的是,SQL Hint 展示的详细 Cost 信息是基于统计信息估算而来,需要确保统计信息已开启收集且准确。此外,Cost 数值表示的是估算数据,这些数值和数据库的实际值可能存在一定偏差,仅供分析问题时参考使用。
mysql> /* SPHEREEX_HINT: SHOW_EXPLAIN_COST=true */ EXPLAIN SELECT * FROM t_order o INNER JOIN t_order_item i ON o.order_id = i.order_id WHERE o.user_id = 10 ORDER BY o.order_id, 7;
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| PLAN |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| DBPlusEngineSort(sort0=[$0], sort1=[$6], dir0=[ASC], dir1=[ASC]): rowcount = 1.0, cumulative cost = DBPlusEngineCost{rowCount=4.0, cpu=10.0, memory=298.0, io=2.0, network=0.0, cost=17.8203125} |
| DBPlusEngineSortMergeJoin(condition=[=($0, $7)], joinType=[inner]): rowcount = 1.0, cumulative cost = DBPlusEngineCost{rowCount=3.0, cpu=6.0, memory=0.0, io=2.0, network=0.0, cost=8.0} |
| DBPlusEngineScan(table=[[sphereex_db_tbl_sql_federation, t_order]], sql=[SELECT * FROM `sphereex_db_tbl_sql_federation`.`t_order` WHERE `user_id` = 10 ORDER BY `order_id`]): rowcount = 1.0, cumulative cost = DBPlusEngineCost{rowCount=1.0, cpu=2.0, memory=0.0, io=1.0, network=0.0, cost=3.0} |
| DBPlusEngineScan(table=[[sphereex_db_tbl_sql_federation, t_order_item]], sql=[SELECT * FROM `sphereex_db_tbl_sql_federation`.`t_order_item` ORDER BY `order_id`]): rowcount = 1.0, cumulative cost = DBPlusEngineCost{rowCount=1.0, cpu=2.0, memory=0.0, io=1.0, network=0.0, cost=3.0} |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
4 rows in set (1.34 sec)
开启统计信息收集 #
联邦查询功能依赖统计信息,因此需要开启统计信息收集功能,并将统计信息存储到 DB 中。通过在 Proxy 上执行如下的 DistSQL 修改统计信息存储规则(如下数据库账号按照实际情况修改)。
ALTER STATISTICS_STORAGE RULE (
NAME_PREFIX=sphereex,
STORAGE UNIT (
URL = "jdbc:mysql://127.0.0.1:3306?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true",
USER = "root",
PASSWORD = "123456",
PROPERTIES("minPoolSize"=1, "maxPoolSize"=2)
)
);
然后在 MySQL 中执行 Grant 语句给 STATISTICS_STORAGE Rule 中的账号赋权(如下数据库账号按照实际情况修改)。
GRANT CREATE, SELECT, INSERT, UPDATE, DELETE, DROP ON sphereex_information_schema.* TO 'root'@'%';
GRANT CREATE, SELECT, INSERT, UPDATE, DELETE, DROP ON sphereex_shardingsphere.* TO 'root'@'%';
再执行 DistSQL 设置统计信息收集间隔,并开启统计信息收集功能(生产环境的时间间隔需要考虑数据的变化情况,通常 1 天更新 1 次即可)。
SET DIST VARIABLE proxy_meta_data_collector_cron = '0 0/30 * * * ?';
SET DIST VARIABLE proxy_meta_data_collector_enabled = 'true';
稍等一会执行如下的 SQL 检查统计信息收集的数据,返回数据则表示统计信息收集成功。
SELECT * FROM shardingsphere.table_statistics;
SELECT * FROM shardingsphere.column_statistics;
当通过定时任务、脚本等批量更新时,需要主动执行 REFRESH STATISTICS METADATA; 更新统计信息。
REFRESH STATISTICS METADATA;
JDBC 接入端接入 #
完成统计信息收集后,JDBC 接入端可以接入联邦查询,接入方式比较简单,只需要在指定的目录下配置 global.yaml 文件,和 Proxy 接入端使用相同的注册中心。
mode:
type: Cluster
repository:
type: ZooKeeper
props:
namespace: governance_ds_sql_federation
server-lists: localhost:2181
retryIntervalMilliseconds: 500
timeToLiveSeconds: 60
maxRetries: 3
operationTimeoutMilliseconds: 500
然后在相同的目录下创建多个逻辑库对应的 yaml 文件,例如:config-sphereex_db_tbl_sql_federation.yaml、config-sphereex_product.yaml 和 config-sphereex_data_type.yaml,yaml 文件中配置 Proxy 接入端创建的逻辑库名称,在 JDBC 启动时会从注册中心拉取对应逻辑库中的配置。
# config-sphereex_db_tbl_sql_federation.yaml 配置
databaseName: sphereex_db_tbl_sql_federation
# config-sphereex_product.yaml 配置
databaseName: sphereex_product
# config-sphereex_data_type.yaml 配置
databaseName: sphereex_data_type
最后修改应用程序连接池属性中的 url 和 driver 参数,url 中指定配置文件的目录 /dbplusengine/config,更多 JDBC 接入方式参考 YAML 存储方式:
driverClassName: com.sphereex.dbplusengine.driver.SphereExDriver
jdbcUrl: jdbc:sphereex:absolutepath:/dbplusengine/config?databaseName=sphereex_db_tbl_sql_federation
实践建议 #
推荐资源配置 #
应用接入联邦查询时,需要根据不同的接入方式,评估应用的资源配置是否合理。当应用通过 Proxy 接入联邦查询时,应用端无需增加资源配置,只需要在 Proxy 中配置好资源即可,通常 Proxy 推荐的入门配置为 8C 16G,需要结合业务并发量压测再调整 Proxy 配置、Proxy 实例数。
如果应用采用 JDBC 方式接入联邦查询,由于 JDBC 是以 jar 包方式内置在应用程序中,会占用部分应用的 CPU 和内存资源。JDBC 接入时,应用的入门配置为 4C 4G,小于这个配置的,建议先升级配置,然后再基于业务并发量,开启联邦查询功能进行压测,测试该配置是否满足业务要求。
使用 JDBC 方式接入联邦查询功能时,由于 JDBC 相比 Proxy 资源更少,建议 MAX_USAGE_MEMORY_PER_QUERY 参数不要设置过大,当业务请求并发量较高时(联邦查询使用内存 = 并发数 * MAX_USAGE_MEMORY_PER_QUERY),可能会占用过多 JDBC 内存,更严重可能会导致 OOM,需要进行充分评估。
不推荐使用场景 #
以下不推荐场景在 JDBC 接入中需要特别注意,JDBC 可用资源少,问题会更加显著。
- SQL JOIN 表数量不建议超过 5,过多的 JOIN 表会导致优化器搜索空间过大,SQL 优化的耗时长,并且很难获得最优执行计划;
- 不推荐使用非等值关联条件,例如
SELECT * FROM t_order o JOIN t_order_item i ON o.order_id > i.order_id,非等值条件关联会使用 NestedLoopJoin 方式执行,会占用较多的内存(或磁盘)和 CPU 资源; - 不推荐 JOIN 关联时不指定 ON 关联条件,例如:
SELECT * FROM t_order o JOIN t_order_item i,不带 ON 关联条件,会使用 NestedLoopJoin 方式执行,并生成笛卡尔积,会占用较多的内存(或磁盘)和 CPU 资源; - 不推荐对大数据集合进行排序操作(内核支持的单表排序除外),联邦查询场景下对大数据集合进行排序,会使用外部文件的排序方式,涉及到多次磁盘读写,执行效率较低;
- 不推荐对大数据集合进行聚合、取交集、取差集,会导致大量数据交换到磁盘,涉及到多次磁盘读写,执行效率较低;
- 不推荐使用相关子查询(即子查询依赖外层查询的每行数据),由于子查询依赖外层查询的每行数据,执行效率较低;
SELECT *
FROM t_order o
WHERE EXISTS (
SELECT 1
FROM t_order_item i
-- 相关子查询依赖外层 o.order_id
WHERE i.order_id = o.order_id
);
- 不推荐使用
UPDATE JOIN语句进行关联更新,将当前列更新为其他表的列值,例如:UPDATE t1 JOIN t2 ON t1.id = t2.id SET t1.col1 = t2.col2;,在此场景下,虽然联邦查询会使用 JDBC 中的executeBatch接口批量更新,但考虑到更新是基于数据行进行,性能可能会不佳,不推荐在性能敏感的场景中使用该语法。
FAQ #
联邦查询和 DBPlusEngine 内核如何划分执行的 SQL 范围? #
联邦查询引擎支持全部的查询 SQL,但由于联邦查询引擎需要将数据加载到内存计算,它的性能不如内核的下推引擎,因此 DBPlusEngine 通过 SQL 决断引擎在内部进行路由,DBPlusEngine 内核支持的 SQL 仍然由内核执行,对于内核不支持的复杂 SQL,交由联邦查询引擎执行,这样可以充分发挥不同 SQL 引擎的优势,即保证核心的 TP 场景 SQL 高性能,也能提升水平分片场景下的 SQL 支持度。