亚马逊AWS官方博客

基于 DMS 进行 DDL 同步的测试与分析

在当前云化快速发展的时代下,数据迁移和复制已经成为企业日常业务运维中重要的一环。其中,AWS 数据库迁移服务(AWS Database Migration Service,简称 DMS)作为一款高度可用、灵活的数据库迁移方案,帮助众多企业快速、安全地完成数据的异构迁移任务。AWS DMS 支持不同数据库引擎间的迁移,例如 Oracle、SQL Server、MySQL、PostgreSQL,以及 Amazon Aurora 等多种数据库的相互复制和迁移,极大地简化了传统数据库迁移流程。

除了数据库迁移场景,AWS DMS 也被广泛用于数据库之间的持续复制,比如多个数据库的数据汇总,如下图所示。相比于使用 RDS 数据库的只读副本,AWS DMS 可以将多个 RDS 数据库实例的不同数据表,汇总到同一个 RDS 数据库实例,提升资源的利用率。

但在实际生产环境中,数据库结构的变化,如新增数据表、字段修改、索引维护等操作(统称为 DDL 操作),都是不可避免的。所以,源端业务系统的升级所引起的数据库 Schema 变更带来了挑战,困扰了许多用户。AWS DMS 提供了高效的 DDL 同步特性,自动捕获并复制源数据库的 DDL 更改到目标数据库。例如常见的新增表、字段修改等操作,DMS 均可实时识别并处理这些变化。但就如何使用 DMS 的能力特性方面,客户也可能遇到以下疑问:

  • 针对特定数据库,有哪些 DDL 不支持自动同步?
  • 进行 DDL 变更时,要不要停止 DMS 任务?
  • 若停止了 DMS 任务,做完了 DDL 变更,是恢复任务还重启任务?
  • 什么场景下需要手动在源与目标进行 DDL 操作?

本文将以 RDS MySQL 为例,验证 AWS DMS 在 DDL 同步方面的能力,并探索其功能边界与限制。如下图所示,罗列了本文将验证的场景。

一、测试场景:启用 DMS DDL 同步

在本测试场景中,先启用 DMS DDL 同步特性后,再进行各用例的测试。相关设置如下所示。

源与目标数据库引擎:RDS MySQL 8.0.40

DMS 复制实例版本:3.5.3

DMS 迁移任务的目标表模式:Truncate

启用 DDL 同步,需要在 DMS 迁移任务中进行如下配置:

"ChangeProcessingDdlHandlingPolicy": {
"HandleSourceTableDropped": true,
"HandleSourceTableTruncated": true,
"HandleSourceTableAltered": true
},

1. 测试用例:直接在源数据库进行 DDL 变更

本测试用例中,将在迁移任务运行的状态下,直接在源端进行 DDL 操作。测试脚本如下所示:

-- 启动DMS迁移任务前:在源与目标同步进行初始化
DROP TABLE IF EXISTS testtb;
CREATE TABLE IF NOT EXISTS testtb (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb(id, name) values (1, 'test1');
insert into testtb(id, name) values (2, 'test2');
insert into testtb(id, name) values (3, 'test3');

-- 启动DMS迁移任务后: 末尾添加字段
ALTER TABLE testtb ADD COLUMN `desc` VARCHAR(255);

-- 启动DMS迁移任务后: 修改字段名
ALTER TABLE testtb CHANGE COLUMN name namestr VARCHAR(50) NOT NULL;
insert into testtb(id, namestr, `desc`) values (4, 'test4', 'test4 desc');

-- 启动DMS迁移任务后: 修改字段类型
ALTER TABLE testtb MODIFY COLUMN namestr VARCHAR(100);

-- 启动DMS迁移任务后: 删除字段
ALTER TABLE testtb DROP COLUMN `desc`;

-- 启动DMS迁移任务后: truncate表
TRUNCATE TABLE testtb;

-- 启动DMS迁移任务后: 新增索引
ALTER TABLE testtb ADD INDEX idx_name (namestr);
SHOW INDEXES FROM testtb;

-- 启动DMS迁移任务后: 在中间添加字段
ALTER TABLE testtb ADD COLUMN `desc2` VARCHAR(255) AFTER `id`;

-- 启动DMS迁移任务后: 重命名表
ALTER TABLE testtb RENAME TO testtb2;

-- 启动DMS迁移任务后: 新增表
CREATE TABLE IF NOT EXISTS testtb3 (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb3(id, name) values (5, 'test1');
insert into testtb3(id, name) values (6, 'test2');
insert into testtb3(id, name) values (7, 'test3');

-- 启动DMS迁移任务后: 删除表
drop table testtb3;

测试结果如下:

序号 测试用例 结果 备注
1 末尾添加列 通过
2 修改字段名 通过
3 修改字段类型 通过
4 删除字段 通过
5 truncate 表 通过
6 新增索引 不通过
7 在中间添加字段 不通过 在源表字段中间新增字段,到了目标表变为在末尾新增字段
8 新增表 通过
9 重命名表 不通过
10 删除表 不通过

2. 测试用例:关停迁移任务→在源数据库进行 DDL 操作→恢复任务

本用例中做了两次测试,第一次做全用例的测试,第二次剔除失败的 DDL 操作后,再进行测试。

第一次测试

本测试用例中,将在迁移任务关闭的状态下,在源端数据库进行 DDL 变更,然后再恢复 DMS 任务。测试脚本如下所示:

-- 启动DMS迁移任务前:在源与目标同步进行初始化
DROP TABLE IF EXISTS testtb;
CREATE TABLE IF NOT EXISTS testtb (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb(id, name) values (1, 'test1');
insert into testtb(id, name) values (2, 'test2');
insert into testtb(id, name) values (3, 'test3');

---------以下 SQL 在启动DMS迁移任务->再停止任务后,仅在源数据库执行----------------
-- 末尾添加字段
ALTER TABLE testtb ADD COLUMN `desc` VARCHAR(255);

-- 修改字段名
ALTER TABLE testtb CHANGE COLUMN name namestr VARCHAR(50) NOT NULL;
insert into testtb(id, namestr, `desc`) values (4, 'test4', 'test4 desc');

-- 修改字段类型
ALTER TABLE testtb MODIFY COLUMN namestr VARCHAR(100);

-- 删除字段
ALTER TABLE testtb DROP COLUMN `desc`;

-- truncate表
TRUNCATE TABLE testtb;

-- 新增索引
ALTER TABLE testtb ADD INDEX idx_name (namestr);
SHOW INDEXES FROM testtb;

-- 在中间添加字段
ALTER TABLE testtb ADD COLUMN `desc2` VARCHAR(255) AFTER `id`;

-- 启动DMS迁移任务后: 重命名表
ALTER TABLE testtb RENAME TO testtb2;

-- 新增表
CREATE TABLE IF NOT EXISTS testtb3 (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb3(id, name) values (5, 'test1');
insert into testtb3(id, name) values (6, 'test2');
insert into testtb3(id, name) values (7, 'test3');

-- 删除表
drop table testtb3;

---------------------最后再恢复 DMS 任务-------------------------------------------

测试结果:报错,详情如下所示:

第二次测试

分析上述日志,从日志分析,提示找不到 testtb,怀疑与 DDL 中的表重命名与字段重命名有关。参考测试用例 1 中的结果,在上述测试脚本的基础上,去除脚本中表重命,字段重命名,新增中间字段,新增索引后,重新进行测试。测试脚本如下所示:

-- 启动DMS迁移任务前:在源与目标同步进行初始化
DROP TABLE IF EXISTS testtb;
CREATE TABLE IF NOT EXISTS testtb (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb(id, name) values (1, 'test1');
insert into testtb(id, name) values (2, 'test2');
insert into testtb(id, name) values (3, 'test3');

---------以下 SQL 在启动DMS迁移任务->再停止任务后,仅在源数据库执行----------------
-- 末尾添加字段
ALTER TABLE testtb ADD COLUMN `desc` VARCHAR(255);

-- 修改字段类型
ALTER TABLE testtb MODIFY COLUMN name VARCHAR(100);

-- 删除字段
ALTER TABLE testtb DROP COLUMN `desc`;

-- truncate表
TRUNCATE TABLE testtb;

-- 新增表
CREATE TABLE IF NOT EXISTS testtb3 (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb3(id, name) values (5, 'test1');
insert into testtb3(id, name) values (6, 'test2');
insert into testtb3(id, name) values (7, 'test3');

-- 删除表
drop table testtb3;
---------------------最后再恢复 DMS 任务-------------------------------------------

测试结果:通过。 恢复 DMS 任务后,所有状态正常,后续的数据复制也正常。然后又分别针对重命名表、重命名字段、在中间插入字段,单独进行测试(停止任务→单独执行→恢复任务)。

  • 重命名表:通过,但在目标数据库中是按新命名新建表、并未删除原有表
  • 重命名字段:通过
  • 在中间插入字段:不通过

3. 测试用例:停止迁移任务→在源和目标数据库同时进行 DDL 操作→恢复任务

本测试用例中,将在迁移任务关闭的状态下,同时在源端和目标端数据库进行 DDL 变更。结合前面的测试结论,我们剔除了一些确定不支持的 DDL,如字段重命名,表重命名,测试脚本如下所示:

-- 启动DMS迁移任务前:在源与目标同步进行初始化
DROP TABLE IF EXISTS testtb;
CREATE TABLE IF NOT EXISTS testtb (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb(id, name) values (1, 'test1');
insert into testtb(id, name) values (2, 'test2');
insert into testtb(id, name) values (3, 'test3');

---------以下 SQL 在启动DMS迁移任务->再停止任务后,在源和目标数据库同步执行----------------
-- 末尾添加字段
ALTER TABLE testtb ADD COLUMN `desc` VARCHAR(255);

-- 修改字段类型
ALTER TABLE testtb MODIFY COLUMN name VARCHAR(100);

-- 删除字段
ALTER TABLE testtb DROP COLUMN `name`;

-- 添加索引
ALTER TABLE testtb ADD INDEX idx_name (desc);
SHOW INDEXES FROM testtb;

-- truncate表
TRUNCATE TABLE testtb;

-- 新增表
CREATE TABLE IF NOT EXISTS testtb3 (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb3(id, name) values (5, 'test1');
insert into testtb3(id, name) values (6, 'test2');
insert into testtb3(id, name) values (7, 'test3');

-- 删除表
drop table testtb3;

---------------------最后再恢复 DMS 任务-------------------------------------------

测试结果:报错。并且根据日志提示,在测试脚本中剔除了删除字段的 SQL 后再次测试,仍然报错。

二、测试场景: 关闭 DMS DDL 同步

在本测试场景中,先关闭 DMS DDL 同步特性后,再进行各用例的测试。相关设置如下所示。

源与目标数据库引擎:RDS MySQL 8.0.40

DMS 复制实例版本:3.5.3

DMS 迁移任务的目标表模式:Truncate

关闭 DDL 同步,需要在 DMS 迁移任务中进行如下配置:

"ChangeProcessingDdlHandlingPolicy": {
"HandleSourceTableDropped": false,
"HandleSourceTableTruncated": false,
"HandleSourceTableAltered":false
},

测试用例:停止迁移任务→在源和目标数据库同时进行 DDL 操作→恢复任务

本用例中做了两次测试,第一次做全用例的测试,第二次剔除失败的 DDL 操作后,再进行测试。

第一次测试

本测试用例中,将在迁移任务关闭的状态下,同时在源端和目标端数据库进行 DDL 变更。测试脚本如下所示:

-- 启动DMS迁移任务前:在源与目标同步进行初始化
DROP TABLE IF EXISTS testtb;
CREATE TABLE IF NOT EXISTS testtb (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb(id, name) values (1, 'test1');
insert into testtb(id, name) values (2, 'test2');
insert into testtb(id, name) values (3, 'test3');

---------以下 SQL 在启动DMS迁移任务->再停止任务后,在源和目标数据库同步执行----------------
-- 末尾添加字段
ALTER TABLE testtb ADD COLUMN `desc` VARCHAR(255);

-- 修改字段名
ALTER TABLE testtb CHANGE COLUMN name namestr VARCHAR(50) NOT NULL;
insert into testtb(id, namestr, `desc`) values (4, 'test4', 'test4 desc');

-- 修改字段类型
ALTER TABLE testtb MODIFY COLUMN namestr VARCHAR(100);

-- 删除字段
ALTER TABLE testtb DROP COLUMN `desc`;

-- truncate表
TRUNCATE TABLE testtb;

-- 新增索引
ALTER TABLE testtb ADD INDEX idx_name (namestr);
SHOW INDEXES FROM testtb;

-- 在中间添加字段
ALTER TABLE testtb ADD COLUMN `desc2` VARCHAR(255) AFTER `id`;

-- 启动DMS迁移任务后: 重命名表
ALTER TABLE testtb RENAME TO testtb2;

-- 新增表
CREATE TABLE IF NOT EXISTS testtb3 (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb3(id, name) values (5, 'test1');
insert into testtb3(id, name) values (6, 'test2');
insert into testtb3(id, name) values (7, 'test3');

-- 删除表
drop table testtb3;

---------------------最后再恢复 DMS 任务-------------------------------------------

测试结果:报错,如下图所示。

第二次测试

从日志分析,提示找不到 testtb,怀疑与 DDL 中的表重命名与字段重命名有关。参考测试场景一的测试结果,在上述测试脚本的基础上,去除脚本中表重命名与字段重命名 SQL 后,重新进行测试。

-- 启动DMS迁移任务前:在源与目标同步进行初始化
DROP TABLE IF EXISTS testtb;
CREATE TABLE IF NOT EXISTS testtb (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb(id, name) values (1, 'test1');
insert into testtb(id, name) values (2, 'test2');
insert into testtb(id, name) values (3, 'test3');

---------以下 SQL 在启动DMS迁移任务->再停止任务后,在源和目标数据库同步执行----------------
-- 末尾添加字段
ALTER TABLE testtb ADD COLUMN `desc` VARCHAR(255);

-- 修改字段类型
ALTER TABLE testtb MODIFY COLUMN namestr VARCHAR(100);

-- 删除字段
ALTER TABLE testtb DROP COLUMN `desc`;

-- truncate表
TRUNCATE TABLE testtb;

-- 新增索引
ALTER TABLE testtb ADD INDEX idx_name (namestr);
SHOW INDEXES FROM testtb;

-- 在中间添加字段
ALTER TABLE testtb ADD COLUMN `desc2` VARCHAR(255) AFTER `id`;


-- 新增表
CREATE TABLE IF NOT EXISTS testtb3 (
    id INT PRIMARY KEY ,
    name VARCHAR(50) NOT NULL
);
insert into testtb3(id, name) values (5, 'test1');
insert into testtb3(id, name) values (6, 'test2');
insert into testtb3(id, name) values (7, 'test3');

-- 删除表
drop table testtb3;

---------------------最后再恢复 DMS 任务-------------------------------------------

测试结果:通过。恢复 DMS 任务后,所有状态正常,后续的数据复制也正常。然后又分别针对重命名表、重命名字段、在中间插入字段,单独进行测试(停止任务→单独执行→恢复任务)。

  • 重命名表:通过
  • 重命名字段:通过

三、总结&综述

在 AWS DMS 开启 DDL 的情况下,测试结果如下所示。

序号 测试用例 结果 备注
1. 在 DMS 任务运行状态下直接在源端进行 DDL 操作 末尾添加字段 通过
修改字段名 通过
修改字段类型 通过
删除字段 通过
 truncate 表 通过
新增索引 不通过
在中间添加字段 不通过 在源表字段中间新增字段,到了目标表变为在末尾新增列
新增表 通过
重命名表 不通过
删除表 不通过
2. 在 DMS 任务停止状态下在源端进行 DDL 操作,然后恢复 末尾添加字段 通过
修改字段名 不通过 单独执行,可正常通过
修改字段类型 通过
删除字段 通过
 truncate 表 通过
新增索引 不通过
在中间添加字段 不通过
新增表 通过
重命名表 不通过 单独执行,可正常通过,但目标库的旧名称表格会保留
删除表 不通过
3. 在 DMS 任务停止状态下在源端和目标端进行 DDL 操作,然后恢复 末尾添加字段 不通过
修改字段名 不通过
修改字段类型 不通过
删除字段 不通过
 truncate 表 不通过
新增索引 不通过
在中间添加字段 不通过
新增表 不通过
重命名表 不通过
删除表 不通过

在AWS DMS 关闭 DDL 同步的情况下,测试结果如下所示。

序号 测试用例 结果 备注
4. 在 DMS 任务停止状态下直接在源和目标端进行 DDL 操作 末尾添加字段 通过
修改字段名 不通过 单独执行,可正常通过;与“末尾添加字段”、“修改字段类型”、“删除字段”、“truncate 表”混合执行时也可通过
修改字段类型 通过
删除字段 通过
 truncate 表 通过
新增索引 通过
在中间添加字段 通过
新增表 通过
重命名表 不通过 单独执行,可正常通过
删除表 通过

基于以上的测试结果说明,在使用 AWS DMS 进行 DMS 同步时,建议要么开启 DDL 同步特性、并在任务运行状态下在源端做 DDL 操作,要么在关闭 DDL 同步特性、并在任务停止状态下手动在源与目标端进行 DDL 操作。为了更进一步的说明细节,本文给出以下的决策图。至于决策图中未提及的索引,经过测试无法使用 DDL 同步特性进行同步,但可以直接手动在源和目标进行新增、修改和删除。

综上所述,本文以 MySQL 8.0.40为例,测试了 DMS 对于 DDL 变更的支持,并基于测试结果,给出了实践中的决策建议。但考虑 DMS 在将 DDL 语句应用于特定目标引擎时,处理方式各不相同。所以,在使用 DMS 同步其他数据库引擎时,需要结合 AWS 官方文档与实际测试结果来决策相关配置。

本篇作者

张盼富

亚马逊云科技解决方案架构师,从业十三年,先后经过历云计算、供应链金融、电商等多个行业,担任过高级开发、架构师、产品经理、开发总监等多种角色,有丰富的大数据应用与数据治理经验。加入亚马逊云科技后,致力于通过大数据+AI 技术,帮助企业加速数字化转型。