继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

DM 分库分表 DDL “乐观协调” 模式介绍丨TiDB 工具分享

PingCAP
关注TA
已关注
手记 481
粉丝 61
获赞 84

前言

DM 支持在线执行分库分表的 DDL 语句(通称 Sharding DDL),先前的文章中,我们介绍了悲观模式,即当上游一个分表执行某一 DDL 后,这个分表的迁移会暂停,等待其他所有分表都执行了同样的 DDL 才在下游执行该 DDL 并继续数据迁移。

悲观协调模式的优点是可以保证迁移到下游的数据不会出错,并且能兼容大部分的 DDL 语句,缺点是会暂停数据迁移而不利于对上游进行灰度变更、并显著地增加增量数据复制的延迟。有些客户可能会花数个月在单一分片执行 DDL,满意后才会更改其他分片的结构。在悲观同步的设定下,用来测试的分片的 DML 事件会大量积压,在恢复同步后无法正常运作。与此同时,悲观模式还要求所有分片必须以相同的顺序执行 DDL,否则会导致任务报错暂停。

为此,DM 提供新的乐观协调模式,在一个分表上执行的 DDL,自动修改成兼容其他分表的 DDL 语句后立即应用到下游,不会阻挡任何分表执行的 DML 的迁移。乐观协调模式适用于上游灰度更新、发布的场景,或者是对上游数据库表结构变更过程中同步延迟比较敏感的场景。

1.png

悲观协调和乐观协调的对比

原理

DM worker 的所有 DML 会直接同步到下游(出错时例外)。

DM worker 内嵌了一个小型 TiDB(通称 schema tracker),用来记录各个上游分表的表结构,当接收到来自上游的 DDL 后,会根据 schema tracker 里 DDL 的执行结果,把更新后的表结构转送给 DM master。DM master 将收到的不同分片的表结构合并成可兼容所有分片的 DML 的合成结构,即不同分片表结构的并集(此过程类似于 SQL 语句中的 JOIN 语句),然后根据合成的表结构和 DM worker 发来的表结构的不同处得到对应的 DDL 语句(即合成的表结构与原表结构的差集),同步到下游。

2.png

(具体的设计可以参考 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

对于被不存在或者被删除的列,我们把它定为最小的列

如初始时表结构是相同的。

3.png

tbl2 添加第三列。前两列相同;tbl1 的第三列为空,所以保留 tbl2 的第三列。

4.png

tbl2 删除第一列。第二列相同;tbl2 的第一列为空,所以保留 tbl1 的第一列。tbl1 的第三列为空,所以保留 tbl2 的第三列

5.png

tbl1 将第二列改为 varchar(10),由于 varchar(5) < varchar(10),所以保留 tbl1 的第二列

6.png

tbl1 重命名第二列。现在 tbl1 和 tbl2 的第二列名字不一样,无法比较,DM 无法确定最终的表结构,所以任务会报错

7.png

例子

三个分片合并同步到 TiDB

8.png

① 在上游增加一列 Level。
alter table tbl00 add column Level int unsigned not null;

9.png

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;

10.png

这时候各种 DML 毋需修改都可以同步到下游。
update tbl00 set Level = 9 where ID = 1;
insert into tbl02 (ID, Name) values (27, ‘Tony’);

11.png

② 在 tbl01 同样增加一列 Level。
alter table tbl01 add column Level int unsigned not null;

12.png

tbl00, tbl01, tbl02 的并集 tblMerge 是 {ID,NAME,Level}
tblMerge 和 tbl 的差集是 {},所以 DDL 为空

此时下游已经有相同的 Level 列了,所以 DM master 比较之后不做任何动作。

③ 在 tbl01 刪除一列 Name。
alter table tbl01 drop column Name;

13.png

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;

14.png

④ 在 tbl02 增加一列 Level。
tbl00, tbl01, tbl02 的并集 tblMerge 是 {ID,NAME,Level}
tblMerge 和 tbl 的差集是 {Level},所以 DDL 为空
alter table tbl02 add column Level int unsigned not null;

15.png

此时所有分片都已有 Level 列,所以可以把作为兼容的默认值去掉。
alter table tbl alter column Level drop default;

⑤⑥ 在 tbl00 和 tbl02 各刪除一列 Name。
alter table tbl00 drop column Name;
alter table tbl02 drop column Name;

16.png

tbl00, tbl01, tbl02 的并集 tblMerge 是 {ID,Level}
tblMerge 和 tbl 的差集是 -{Name},此差集是有符号的,所以 DDL 是 drop column Name

到此步 Name 列也从所有分片消失了,所以可以安全从下游移除。
alter table tbl drop column Name;

17.png

限制

使用“乐观协调”模式有一定的风险,需要严格遵照以下方针:

  • 执行每个批次的 DDL 前和后,要确保每个分表的结构达成一致。
  • 进行灰度 DDL 时,最好只集中在一个分表上测试。
  • 灰度完成后,在其他分表上尽量以最简单直接的 DDL 迁移到最终的 schema,而不要重新执行灰度测试中对或错的每一步。
  • 例如:在分表执行过 ADD COLUMN A INT; DROP COLUMN A; ADD COLUMN A FLOAT;,在其他分表直接执行 ADD COLUMN A FLOAT 即可,不需要三条 DDL 都执行一遍。
  • 执行 DDL 时要注意观察 DM 迁移状态。当迁移报错时,需要判断这个批次的 DDL 是否会造成数据不一致。
打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP