概述
TDSQL Boundless 并行查询是内建于计算引擎的并行查询框架。当查询数据量达到一定阈值时,优化器自动将查询计划分解为多个子任务,由多个 Worker 线程并行执行,使复杂查询的响应时间大幅下降。
总体流程
1. SQL 解析与优化:SQL 经过 MySQL 原生优化器生成串行执行计划
2. 并行计划生成:并行查询优化器对串行计划进行评估,判断是否满足并行条件
3. 计划分解:将符合条件的表扫描拆分为多个范围分片,生成 PartialPlan
4. 并行执行:Worker 线程(简称 Worker)各自执行 PartialPlan,独立扫描分配到的数据分片
5. 结果收集:Leader 线程(简称 Leader)通过 Collector 操作汇总各 Worker 的结果,必要时做归并排序或去重
6. 返回客户端:Leader 将最终结果返回客户端
适用场景
多表关联查询:JOIN 查询涉及大表扫描
聚合分析:GROUP BY + 聚合函数的大数据量查询
排序查询:需要对大量数据进行 ORDER BY 排序
分区表查询:按分区键并行扫描各分区
含子查询的复杂 SQL:子查询可并行预先执行
功能原理
并行计划生成
并行计划生成的关键检查包括:
1. 参数限制检查:
max_parallel_workers 必须大于 0、max_parallel_degree 必须有效或存在 PARALLEL HINT,否则直接退出2. 代价评估:检查串行执行代价是否超过
parallel_plan_cost_threshold,parallel_query_switch 中 force=on 或存在 PARALLEL HINT 时跳过此检查3. 并行安全检查:遍历查询中所有表达式,任一表达式不支持并行执行则拒绝并行
4. 分布策略选择:为每张表选择分布类型(详见 并行查询优化器 中的分布模型章节)
5. JOIN 路径组合:对 outer/inner 两侧路径,剪枝不兼容组合,必要时插入 Collector 桥接
6. 最优路径选择:根据代价选择路径,优先选 HINT 匹配最多的路径;系统变量
parallel_plan_compare_serial_cost 控制串行路径是否参与比较7. Slice 划分:沿最优路径在 Collector 处切分,根 Slice 运行于 Leader,每个子 Slice 对应一个 PartialPlan
Slice 与 PartialPlan
并行计划将原始计划按 Slice 进行分片。每个 Slice 是一个独立的执行单元:
Slice 0(Leader 端):在 Leader 上执行,负责汇总结果。包括 Collector 操作和需要全局视角的操作
Slice 1 ~ N(Worker 端):在 Worker 上并行执行,每个 Worker 可能包含并行扫描(支持多张表的并行扫描)
例如一个简单的
SELECT * FROM t1 WHERE a > 0 的并行计划:Slice 1:4 个 Worker 各自扫描 t1 的 1/4 数据范围
Slice 0:Leader 通过 Collector 收集 4 个 Worker 的结果
对于带 JOIN 的查询
SELECT * FROM t1 JOIN t2:Slice 1:4 个 Worker 并行扫描 t1,各自与 t2 在本 Worker 内完成 Hash Join
Slice 0:Leader 通过 Collector 收集 4 个 Worker 的 JOIN 结果
说明:
并行扫描
Dynamic Range Scan(动态范围并行扫描)
将表数据按主键范围动态切分为 N 个连续分片。每个 Worker 负责一个分片的扫描。
适用场景:
未分区或无需按分区分片的普通表
带范围条件的查询(
WHERE id BETWEEN X AND Y),优化器可进一步剪枝无关分片工作方式:
1. 优化器估算表的总行数和数据分布
2. 将 Key 空间均匀切分为 N 个区间(N 远大于系统变量
max_parallel_degree)3. 每个 Worker 从 N 个区间中动态争抢工作,防止数据倾斜
Partition Scan(分区并行扫描)
按物理分区将数据分片,每个分区分配给一个 Worker。
适用场景:
使用 HASH 或 RANGE 分区的分区表
希望利用分区裁剪的查询,例如针对分区键的等值 JOIN(
WHERE partition_key = X)工作方式:
1. 优化器识别表中可用的分区列表
2. 将各分区均匀分配给 Worker
3. Worker 扫描分配给自己的分区
并行扫描方式选择:
优化器决策:
分区表且分区数足够多(≥ 并行度),优先使用 Partition Scan
非分区表或分区数少,使用 Dynamic Range Scan
通过 HINT 显式指定:
SELECT /*+ PARALLEL(t PARTITION) */ 或 SELECT /*+ PARALLEL(t DYNAMIC_RANGE) */,详细参考 优化器 HintsGather(结果收集)
Gather 是 Leader 线程从多个 Worker 收集结果的算子,它属于一种 Collector。它位于 Leader 端计划的关键位置,负责将并行执行的结果汇总。执行计划中 Gather 节点的具体输出格式详见 并行计划解读。
Gather 支持多种数据处理模式:
模式 | 适用场景 | 说明 |
普通 Gather | 无需保证顺序 | 简单将各 Worker 结果逐行收集并传递给上层算子 |
Merge Sort | 需要 ORDER BY 输出 | 收集时对各 Worker 已排序的结果做归并排序。Worker 端做局部排序,Leader 端做归并 |
Gather 也支持对结果进行流式或物化输出(上层算子需要重复扫描时物化)。
并行聚合
GROUP BY 聚合操作在并行执行中有多种策略,优化器根据查询特征自动选择最优方案。
一阶段聚合
Worker 只负责扫描数据,数据汇聚到 Leader 后,由 Leader 执行聚合。
适用场景:
聚合函数或 GROUP BY 表达式不满足下推条件(如带 DISTINCT 的聚合、不可两阶段的 GROUP_CONCAT / LISTAGG 等),无法将聚合下推到 Worker 时的兜底策略。
两阶段聚合(默认策略)
Worker 先执行局部聚合,接着 Gather 汇总, Leader 执行最终聚合。
流程:
1. 各 Worker 并行扫描数据,对各自范围内的数据执行局部 GROUP BY
2. Worker 将局部聚合结果发送给 Leader
3. Leader 对来自所有 Worker 的中间结果再次执行 GROUP BY,得到最终聚合结果
完全下推聚合
当优化器可以确定各 Worker 间的 GROUP BY 键值不会重复时,将聚合完全下推到 Worker。此时 Leader 无需再次 GROUP BY,只需做简单汇总。
开启条件:
parallel_query_switch 中 full_grouping_pushdown 选项已开启(默认开启)聚合函数和 GROUP BY 表达式满足下推安全性
数据的分布与 GROUP BY 表达式兼容,每个 Worker 上的分组数据完整
对于 Partition Scan,需要分区键是 GROUP BY 列的前缀
对于 Dynamic Range Scan,需要表上的 Distinct Index 与 GROUP BY 列有公共前缀
并行排序
下推排序(默认)
Worker 先对各自结果执行排序,Leader 通过 Gather Merge Sort 归并:
1. 各 Worker 使用本地排序算法(filesort)对各自数据排序
2. Leader 启动 Merge Sort,同时从 N 个 Worker 的有序结果流中读取
3. 通过优先队列按 sortkey 升序归并,逐行返回
优势:充分利用多核并行排序能力,加速大数据量排序
非下推排序
当排序列无法下推(排序表达式包含非并行安全的函数、排序列是常量),数据先通过 Collector 到 Leader,再在 Leader 上串行排序。