前言
DM 反对在线执行分库分表的 DDL 语句(通称 Sharding DDL),先前的文章中,咱们介绍了乐观模式,即当上游一个分表执行某一 DDL 后,这个分表的迁徙会暂停,期待其余所有分表都执行了同样的 DDL 才在上游执行该 DDL 并持续数据迁徙。
乐观协调模式的长处是能够保障迁徙到上游的数据不会出错,并且能兼容大部分的 DDL 语句,毛病是会暂停数据迁徙而不利于对上游进行灰度变更、并显著地减少增量数据复制的提早。有些客户可能会花数个月在繁多分片执行 DDL,称心后才会更改其余分片的构造。在乐观同步的设定下,用来测试的分片的 DML 事件会大量积压,在复原同步后无奈失常运作。与此同时,乐观模式还要求所有分片必须以雷同的程序执行 DDL,否则会导致工作报错暂停。
为此,DM 提供新的乐观协调模式,在一个分表上执行的 DDL,主动批改成兼容其余分表的 DDL 语句后立刻利用到上游,不会阻挡任何分表执行的 DML 的迁徙。乐观协调模式实用于上游灰度更新、公布的场景,或者是对上游数据库表构造变更过程中同步提早比拟敏感的场景。
<center> 乐观协调和乐观协调的比照 <center>
原理
DM worker 的所有 DML 会间接同步到上游(出错时例外)。
DM worker 内嵌了一个小型 TiDB(通称 schema tracker),用来记录各个上游分表的表构造,当接管到来自上游的 DDL 后,会依据 schema tracker 里 DDL 的执行后果,把更新后的表构造转送给 DM master。DM master 将收到的不同分片的表构造合并成可兼容所有分片的 DML 的合成构造,即不同分片表构造的并集(此过程相似于 SQL 语句中的 JOIN 语句),而后依据合成的表构造和 DM worker 发来的表构造的不同处失去对应的 DDL 语句(即合成的表构造与原表构造的差集),同步到上游。
(具体的设计能够参考 [DM: Manage DDLs on Sharded Tables by Maximizing Schema Compatibility]
规定
乐观 DDL 表构造合并的规定简略来说就是对列属性定义了一个 偏序关系,对不同表的同一列进行排序,抉择该偏序关系中的极大元。对于不可比拟的列,则返回谬误
- null < not null
- no default < default(x)
- varchar(x) < varchar(y), where x< y
- utf8 < utf8mb4
- char < varchar
- tinyint < smallint < mediumint < bigint
- …
对于被不存在或者被删除的列,咱们把它定为最小的列
如初始时表构造是雷同的。
tbl2 增加第三列。前两列雷同;tbl1 的第三列为空,所以保留 tbl2 的第三列。
tbl2 删除第一列。第二列雷同;tbl2 的第一列为空,所以保留 tbl1 的第一列。tbl1 的第三列为空,所以保留 tbl2 的第三列
tbl1 将第二列改为 varchar(10),因为 varchar(5) < varchar(10),所以保留 tbl1 的第二列
tbl1 重命名第二列。当初 tbl1 和 tbl2 的第二列名字不一样,无奈比拟,DM 无奈确定最终的表构造,所以工作会报错
例子
三个分片合并同步到 TiDB
① 在上游减少一列 Level。
alter table tbl00
add column Level
int unsigned not null;
tbl00, tbl01, tbl02 的并集 tblMerge 是 {ID,NAME,Level}
tblMerge 和 tbl 的差集是 {Level},所以 DDL 是 add column Level
此时上游 TiDB 要筹备承受来自 tbl00 有 Level 的 DML、以及来自 tbl01 和 tbl02 没有 Level 的 DML,所以同步到上游时,主动改写成指定默认值的模式。
alter table tbl
add column Level
int unsigned not null default 0;
这时候各种 DML 毋需批改都能够同步到上游。
update tbl00
set Level
= 9 where ID
= 1;
insert into tbl02
(ID
, Name
) values (27, ‘Tony’);
② 在 tbl01 同样减少一列 Level。
alter table tbl01
add column Level
int unsigned not null;
tbl00, tbl01, tbl02 的并集 tblMerge 是 {ID,NAME,Level}
tblMerge 和 tbl 的差集是 {},所以 DDL 为空
此时上游曾经有雷同的 Level 列了,所以 DM master 比拟之后不做任何动作。
③ 在 tbl01 刪除一列 Name。
alter table tbl01
drop column Name
;
tbl00, tbl01, tbl02 的并集 tblMerge 是 {ID,NAME,Level}
tblMerge 和 tbl 的差集是 {Level},所以 DDL 为空
此时上游仍须要接管来自 tbl00 和 tbl02 含 Name 的 DMLs,故不立删之,而是为这列也补上一个默认值。
alter table tbl
alter column Name
set default“”;
同样,各种 DML 仍可间接同步到上游。
insert into tbl01
(ID
, Level
) values (15, 7);
update tbl00
set Level
= 5 where ID
= 5;
④ 在 tbl02 减少一列 Level。
tbl00, tbl01, tbl02 的并集 tblMerge 是 {ID,NAME,Level}
tblMerge 和 tbl 的差集是 {Level},所以 DDL 为空
alter table tbl02
add column Level
int unsigned not null;
此时所有分片都已有 Level 列,所以能够把作为兼容的默认值去掉。
alter table tbl
alter column Level
drop default;
⑤⑥ 在 tbl00 和 tbl02 各刪除一列 Name。
alter table tbl00
drop column Name
;
alter table tbl02
drop column Name
;
tbl00, tbl01, tbl02 的并集 tblMerge 是 {ID,Level}
tblMerge 和 tbl 的差集是 -{Name},此差集是有符号的,所以 DDL 是 drop column Name
到此步 Name 列也从所有分片隐没了,所以能够平安从上游移除。
alter table tbl
drop column Name
;
限度
应用“乐观协调”模式有肯定的危险,须要严格遵循以下方针:
- 执行每个批次的 DDL 前和后,要确保每个分表的构造达成统一。
- 进行灰度 DDL 时,最好只集中在一个分表上测试。
-
灰度实现后,在其余分表上尽量以最简略间接的 DDL 迁徙到最终的 schema,而不要从新执行灰度测试中对或错的每一步。
- 例如:在分表执行过 ADD COLUMN A INT; DROP COLUMN A; ADD COLUMN A FLOAT;,在其余分表间接执行 ADD COLUMN A FLOAT 即可,不须要三条 DDL 都执行一遍。
- 执行 DDL 时要留神察看 DM 迁徙状态。当迁徙报错时,须要判断这个批次的 DDL 是否会造成数据不统一。
更具体的介绍可参考官网文档