功能简介
说明:
并行 DML 适合 WHERE 条件命中大量行的场景;若仅修改少量行(如主键点查),串行执行往往更优。
并行 DML 通过把大批量数据拆成多路并行处理,大幅缩短单表更新、删除的耗时,特别适合"一次性动大量数据"的操作。典型场景包括批量数据订正、数据归档清理、定期批处理作业、大表整体维护等。小表、少量行操作或复杂语句无需并行,系统会自动按普通方式执行,结果不变,用户无需调整。
功能限制
仅支持内核 V21.6.4.0 或以上版本的实例开启并行 DML。
仅支持单表的
DELETE/UPDATE 语句的并行执行,即一次只修改或删除一张表,且不与其它表做关联或子查询;暂不支持多表关联的 DELETE/UPDATE 语句的并行执行。仅支持写入普通表,不支持并行 DML 写入临时表、系统表,也不支持带有触发器(TRIGGER)或用户自定义函数(UDF)的表执行并行 DML。
暂不支持部分 UPDATE 类操作开启 DML 并行,包括:
暂不支持目标表含
ON UPDATE CURRENT_TIMESTAMP 列的 UPDATE 语句开启 DML 并行。暂不支持修改主键、唯一索引或分区键的 UPDATE 语句开启 DML 并行。
暂不支持修改扫描用到索引列的 UPDATE 语句开启 DML 并行。
暂不支持 INSERT 类操作,包括:
暂不支持
INSERT INTO ... SELECT ...暂不支持
INSERT ... ON DUPLICATE KEY UPDATE暂不支持
REPLACE暂不支持包括如下写法的 SQL 语句开启并行 DML:
暂不支持带有用户自定义变量的 SQL 语句并行执行,变量的赋值顺序无法保证,语义不可控。
暂不支持带有
IGNORE 的 SQL 语句并行执行,忽略错误的语义与并行执行相冲突。暂不支持包含子查询的 SQL 语句并行执行,子查询的读写依赖关系复杂,无法安全并行。
暂不支持带有 RETURNING 子句的 SQL 语句并行执行,需返回被修改行的结果集,暂不支持并行。
暂不支持带有 LIMIT 子句的 SQL 语句并行执行,并行 DML 仅支持完全下推(读写均并行下推)的执行计划,而 LIMIT 子句天然是部分下推算子,与并行扫描机制冲突。
其他带有并行不安全表达式的 SQL 语句。
当语句不满足上述支持条件时,系统会自动回退为串行执行,执行结果与未开启并行时完全一致,您无需修改任何 SQL。
工作原理
注意:
并行 DML 仅支持完全并行的执行计划(读写均并行下推),若 DML 中包含并行不安全或只能串行执行的函数或者表达式,将会自动回退到串行执行。
并行 DML 的整体执行流程与普通并行查询一致,详见 并行查询概述。核心区别在于:MySQL 原生执行引擎只以算子(迭代器)方式执行“读取”部分,不会以算子(迭代器)方式执行 DML。因此并行 DML 会在查询计划上额外完成如下步骤:
添加 DML 算子:在读取部分的查询计划上添加 DML 算子。
判断能否并行:根据支持场景判断该 DML 是否可并行执行。
并行执行准备:为可并行的 DML 语句补齐并行执行所需结构。
在相同事务隔离级别(REPEATABLE READ 或 READ COMMITTED)下,并行 UPDATE / DELETE 与串行执行结果完全一致:
修改的行及影响行数相同。
锁的获取、等待、超时等行为一致。
跨节点事务中,所有 Worker 读取 Leader 在语句开始时建立的一致性快照,无数据差异。
并行扫描方式
并行查询支持两种并行扫描方式,Dynamic Range Scan(动态范围并行扫描)与 Partition Scan(分区并行扫描),详见 并行查询概述。优化器根据表结构和查询特征自动选择,也可通过 PARALLEL Hint 显式指定。采用 Dynamic Range Scan 方式并行扫描时,有两种分片分配方式,由
parallel_query_switch 的 rep_group_parallel_scan 选项控制(默认关闭)。开启该选项时,并行扫描会按 Replication Group 切分任务,并保证同一个 Replication Group 的数据由同一个并行 worker 处理,从而避免同一个 Replication Group 上并发的带锁读和修改操作导致的性能损耗,适合更新数据量较大的分区表。
关闭该选项时,每个分片被一个 Worker 扫描,worker 根据实际执行的情况去动态获取分片数据。
-- t 表存放在两个 Replication Group 上-- 开启 rep_group_parallel_scanSET parallel_query_switch = 'rep_group_parallel_scan=on';EXPLAIN FORMAT=TREE UPDATE /*+ PARALLEL(4) */ t1 SET a = a + 1;EXPLAIN-> Gather (slice: 1, workers: 2) (cost=2306.04..9455.55 rows=10000)-> Update t1 (immediate) with filter: (cost=1.05..5253.46 rows=5000)-> Table scan on t1, with range parallel scan (cost=0.82..4101.38 rows=5000)-- 关闭 rep_group_parallel_scanSET parallel_query_switch = 'rep_group_parallel_scan=off';EXPLAIN FORMAT=TREE UPDATE /*+ PARALLEL(4) */ t1 SET a = a + 1;EXPLAIN-> Gather (slice: 1, workers: 4) (cost=2305.65..5530.21 rows=10000)-> Update t1 (immediate) (cost=1.00..2511.52 rows=2500)-> Table scan on t1, with range parallel scan (cost=0.77..1935.48 rows=2500)
使用方式
开启并行 DML
说明:
single_delete_update_use_iterator 系统变量可以通过 set_var hint 只在某条 DML 语句中打开。MySQL 原生执行 DML 不经过算子(迭代器)路径,而并行 DML 必须基于算子方式执行。因此,开启并行 DML 需先打开
single_delete_update_use_iterator 系统变量,将 DML 切换到算子执行路径;同时还需在语句中使用 PARALLEL(N) Hint,两者配合才能生效。如下示例展示了两种启动并行 DML 的方式,通过语句 HINT 开启与通过 SESSION 变量开启:-- 通过语句级 HINT 开启算子执行,设置 SQL 执行的并行度为 4UPDATE /*+ set_var(single_delete_update_use_iterator=on) PARALLEL(4) */ t1SET a = a - 1WHERE id > 10;
-- 通过 SESSION 变量开启算子执行,设置 SQL 执行的并行度为 4SET single_delete_update_use_iterator = ON;UPDATE /*+ PARALLEL(4) */ t1SET a = a - 1WHERE id > 10;
显示查询计划验证并行
开启并行 DML 后,可通过
EXPLAIN FORMAT=TREE 查看执行计划,确认 SQL 语句是否真正走了并行,如果计划中出现 Gather (workers: N) 即表示已并行执行。SET single_delete_update_use_iterator = ON;EXPLAIN FORMAT=TREE UPDATE /*+PARALLEL(4) */ t1 SET a = a - 1;EXPLAIN-> Gather (slice: 1, workers: 4) (cost=2305.65..5530.21 rows=10000)-> Update t1 (immediate) (cost=1.00..2511.52 rows=2500)-> Table scan on t1, with range parallel scan (cost=0.77..1935.48 rows=2500)
说明:
原生 MySQL 8.0 默认仅支持通过
EXPLAIN FORMAT=TREE 查看多表 UPDATE / DELETE 语句的执行计划:如果是单表操作,不经过迭代器执行,因此返回
<not executable by iterator executor> 信息:mysql> EXPLAIN FORMAT=TREE DELETE FROM t WHERE id=1;+--------------------------------+| EXPLAIN |+--------------------------------+| <not executable by iterator executor> |+--------------------------------+
如果是多表关联,则正常
EXPLAIN FORMAT=TREE 查看 SQL 语句的执行计划:mysql> EXPLAIN FORMAT=TREE UPDATE t JOIN t2 ON t.id=t2.id SET t.c=t2.v WHERE t2.v>=100;+--------------------------------------------------+| EXPLAIN |+--------------------------------------------------+| -> Update t (immediate) || -> Nested loop inner join (cost=1.60 rows=3)|| -> Table scan on t (cost=0.55 rows=3) || -> Filter: (t2.v >= 100) (cost=0.28 rows=1)|| -> Single-row index lookup on t2 using PRIMARY (id=t.id) |+--------------------------------------------------+
TDSQL Boundless 从21.6.4.0版本开启参数
single_delete_update_use_iterator(默认值 ON)后,单表及多表关联的 UPDATE / DELETE 语句,均可通过 EXPLAIN FORMAT=TREE 查看执行计划。关闭并行 DML
关闭并行 DML 有两种方式,都可以回到串行执行:
关闭算子执行路径:关闭系统变量
single_delete_update_use_iterator = OFF,回退到 MySQL 原生串行执行方式;关闭 SQL 执行并行度:保持系统变量
single_delete_update_use_iterator = ON,去掉 PARALLEL Hint(可同时关闭 parallel_query_switch 的 force 选项),可以回退到串行执行方式。-- 方式一:关闭并行 DML,不使用算子的方式执行 DML 操作,回到 MySQL 原生执行方式SET single_delete_update_use_iterator = OFF;EXPLAIN FORMAT=TREE UPDATE t1 SET a = a - 1;<not executable by iterator executor>-- 方式二:保持算子执行仅不并行,去除 PARALLEL HINT,可以回到串行执行方式SET single_delete_update_use_iterator = ON;EXPLAIN FORMAT=TREE UPDATE t1 SET a = a - 1;EXPLAIN-> Update t1 (immediate) (cost=1.00..10046.08 rows=10000)-> Table scan on t1, with range parallel scan (cost=0.77..7741.94 rows=10000)
常见问题
加了 PARALLEL Hint 但计划仍是串行的?
可能原因:语句含有上文列出的限制(如 LIMIT、子查询)或者无法完全并行的表达式。请检查 OPTIMIZER TRACE 中并行优化部分的
cause,或去掉限制语法后重试。并行 DML 比串行还慢?
并行适合大批量数据的读取和修改。若
WHERE 仅命中少量行,或修改行数很多导致 Leader 写成为瓶颈,串行可能更快。请根据实际需要选择是否开启并行 DML。跨节点表能否使用?
可以。数据分布在多个节点时,各节点 Worker 并行扫描,Leader 统一修改并提交,对用户透明。