AWS DMS实战手记:低风险数据库迁移的分层可控模型
1. 项目概述:这不是一次“点点鼠标”的迁移,而是一场数据库心脏手术的全程实录
我做数据库迁移这行快十二年了,从 Oracle 9i 手动导出导入,到后来用 GoldenGate 搞得半夜三点还在查日志,再到今天 AWS DMS 这套“云上流水线”,踩过的坑比走过的路还多。很多人把 AWS DMS 当成一个“自动搬家工具”——点几下控制台,选个源、选个目标,点“开始”,然后去喝杯咖啡。结果呢?咖啡凉了,任务卡在“Starting”状态,日志里全是Failed to connect to source endpoint;或者更糟,全量数据跑完了,CDC 却像断了线的风筝,滞后三小时还在追,业务方电话已经打爆运维手机。这不是 DMS 的问题,是人对它底层逻辑的误判。
这篇内容,就是我把过去三年里,在金融、电商、SaaS 三个行业落地的 17 个 DMS 迁移项目(最小 200GB,最大 8TB)中,所有被反复验证过、写进 SOP、甚至贴在工位白板上的硬核经验,毫无保留地拆解给你看。它不叫“教程”,它叫《AWS DMS 实战手记》。核心关键词就三个:低风险、可预测、能兜底。低风险,不是指“大概率成功”,而是指你能在迁移前就精确算出失败点在哪、影响面有多大;可预测,是说每一步耗时、每一条日志含义、每一个 CloudWatch 指标波动,你都心里有数;能兜底,是当 CDC 突然停摆、LOB 字段爆内存、或者 Oracle 归档日志被自动清理时,你手里有不止一套应急方案,而不是干等 AWS Support 回邮件。
它适合谁?适合那些已经决定上云、手头正压着一个“必须在下季度初完成迁移”的数据库项目的 DBA 或云架构师;也适合刚接手遗留系统、发现源库是 SQL Server 2005、目标要上 Aurora PostgreSQL 的运维同学;甚至适合想搞懂“为什么我们公司花了 30 万买 DMS 订阅,却还是请外包来干”的技术负责人。它不讲“DMS 是什么”,因为官网文档写得比谁都清楚;它只讲“DMS 在真实世界里,是怎么呼吸、怎么流血、怎么止血的”。接下来的内容,每一行字,都来自生产环境的截图、日志和凌晨四点的 Slack 聊天记录。我们直接进入正题。
2. 核心设计思路:为什么必须放弃“全自动”幻想,拥抱“分层可控”模型
2.1 一个被严重低估的真相:DMS 本身不迁移数据,它只调度“搬运工”
这是所有新手最大的认知陷阱。你看到控制台里那个绿色的“Running”状态,以为 DMS 实例正在疯狂读写磁盘。错。DMS 实例(Replication Instance)本质上是一个智能调度中心 + 日志解析引擎,它自己几乎不碰原始数据块。真正的“苦力”,是它背后调用的两个独立组件:
- Full Load Agent:负责一次性拉取源库全量数据。它会启动多个并行连接(默认 4 个,可调),每个连接按主键范围切片(例如
WHERE id BETWEEN 1 AND 100000),把数据读出来,序列化成 JSON 或 CSV 格式,再发给目标端。 - CDC Agent:这才是 DMS 的灵魂。它不读表,它读的是数据库的事务日志(Transaction Log)。对于 Oracle,它依赖 LogMiner 解析归档日志(Archive Log);对于 MySQL,它解析 binlog;对于 SQL Server,它读取事务日志备份。它把日志里的
INSERT/UPDATE/DELETE操作,翻译成标准的 DML 语句,再推送到目标库。
提示:理解这个分层模型,是解决 80% 问题的钥匙。当你看到“Full Load Throughput”指标飙升,那是 Full Load Agent 在干活;当你看到“CDC Lag”持续增长,那一定是 CDC Agent 在日志解析或网络传输环节卡住了,跟 Full Load Agent 完全无关。很多团队把两个阶段混在一起排查,浪费大量时间。
2.2 “Homogeneous vs Heterogeneous”不是功能开关,而是两套完全不同的技术栈
官方文档把同构(MySQL→MySQL)和异构(Oracle→PostgreSQL)迁移并列介绍,容易让人误以为只是“引擎选个下拉框”的事。实际操作中,它们是两条平行线,技术债和风险点截然不同。
同构迁移(Homogeneous):技术上最“干净”。DMS 只需做协议转换(比如把 MySQL 的 binlog event 映射为 PostgreSQL 的
INSERT ... ON CONFLICT DO NOTHING)。难点在于性能调优和一致性保障。例如,MySQL 的utf8mb4字符集在 PostgreSQL 中需要显式声明ENCODING 'UTF8',否则插入 emoji 会报错;又比如,MySQL 的AUTO_INCREMENT主键,在 PostgreSQL 中要用SERIAL或IDENTITY列模拟,DMS 不会自动帮你建索引,漏了就会导致后续 CDC 更新失败。异构迁移(Heterogeneous):这才是真正的“炼丹炉”。DMS 本身只负责数据流动,Schema 转换、数据类型映射、存储过程重写,全部交给 AWS Schema Conversion Tool(SCT)。而 SCT 的输出,从来不是“开箱即用”的。我经手的一个 Oracle→Aurora PostgreSQL 迁移,SCT 自动生成了 237 个对象,其中:
- 189 个表(Green):DDL 创建成功,但 32 个表的
TIMESTAMP WITH TIME ZONE字段被强制转为TIMESTAMP WITHOUT TIME ZONE,业务方反馈时区计算全乱; - 41 个视图(Blue):SCT 生成了基础
CREATE VIEW,但原 Oracle 视图里嵌套了DBMS_LOB.GETLENGTH()函数,SCT 直接报错,需要手动改写为 PostgreSQL 的LENGTH(); - 7 个存储过程(Red):SCT 完全无法转换,提示
PL/SQL block not supported,最终由 DBA 用 PL/pgSQL 重写了 3 天。
- 189 个表(Green):DDL 创建成功,但 32 个表的
注意:不要迷信 SCT 的“Assessment Report”。它的“Green/Blue/Red”分类,只代表“语法是否能转”,不代表“语义是否等价”。一个
DECODE()函数转成CASE WHEN是 Green,但它在 NULL 处理上的细微差异,可能让下游报表少算 0.3% 的销售额。我的做法是:SCT 报告出来后,第一件事不是点“Convert”,而是把所有 Blue/Red 对象导出为 Excel,拉着业务方和 DBA 开一个 2 小时的“语义对齐会”,逐条确认逻辑。
2.3 “Minimal Downtime” 的代价:你为“零停机”支付的,是双倍的监控、双倍的测试、双倍的预案
DMS 宣传的“minimal downtime”,本质是靠 CDC 的实时追平能力实现的。但这能力不是免费的。它要求你在迁移前,就必须完成三件高成本的事:
源库日志保留策略必须重构:Oracle 默认归档日志只保留 1 天,SQL Server 默认事务日志备份间隔是 15 分钟。而 DMS CDC 要求日志至少保留24-48 小时(金融行业建议 72 小时)。这意味着你要说服 DBA 团队,把原本 100GB 的归档空间,扩容到 500GB 以上,并且要监控
V$ARCHIVED_LOG视图,确保没有DELETED = YES的日志被提前清理。我见过最惨的一次,CDC 运行 12 小时后突然中断,日志显示ORA-01291: missing log file,一查,DBA 的自动清理脚本把 36 小时前的日志全删了。目标库必须预热,且不能是“空库”:很多团队习惯先清空目标库,再跑 DMS。大错特错。CDC 的第一条
INSERT语句,会触发目标库的索引维护、约束检查、触发器执行。如果目标库是空的,这些操作毫秒级完成;但如果目标库已有 1 亿行数据,一个UPDATE可能锁表 2 秒。所以我的标准流程是:Full Load 完成后,立刻对目标库所有大表(>100 万行)执行ANALYZE TABLE(MySQL)或VACUUM ANALYZE(PostgreSQL),让查询优化器更新统计信息,避免 CDC 期间因执行计划劣化导致的慢 SQL。必须有一套独立于 DMS 的“数据一致性快照”机制:DMS 自带的数据验证(Data Validation)功能,只校验行数和校验和(Checksum),它无法发现“源库某条记录的
amount是 100.00,目标库写成了 100.0000000001”这种精度漂移。因此,我在每个关键节点(Full Load 结束、CDC 追平、Cutover 前),都会用pt-table-checksum(Percona Toolkit)或自研的 Python 脚本,对核心业务表(订单、用户、账户)做字段级抽样比对。抽样不是随机的,而是按主键 ID 取模(如id % 1000 = 0),确保覆盖所有数据页。
3. 实操细节与避坑指南:从 VPC 网络到 LOB 处理的 12 个生死关卡
3.1 VPC 网络:90% 的连接失败,根源都在“安全组的第 3 行规则”
DMS 实例、源库、目标库,三者必须在同一个 VPC 内,且安全组(Security Group)规则必须形成闭环。这不是理论,是血泪教训。
DMS 实例的安全组(Inbound):必须放行源库和目标库的数据库端口(Oracle 1521, MySQL 3306, PostgreSQL 5432)。很多人只加了一条
0.0.0.0/0,这是巨大风险。正确做法是:创建一个专门的dms-sg,Inbound 规则只允许来自源库 SG 和目标库 SG 的流量。例如:Type: Custom TCP Protocol: TCP Port Range: 1521 Source: sg-0a1b2c3d4e5f67890 (Source Oracle RDS SG)源库/目标库的安全组(Outbound):必须允许流量发往 DMS 实例的 SG。这里有个致命陷阱:RDS 实例的 Outbound 规则默认是
0.0.0.0/0,看似没问题。但如果你的源库是 EC2 上自建的 Oracle,它的 Outbound 规则如果没配,DMS 实例根本连不上它。我曾为一个客户排查了两天,最后发现是 EC2 的 Outbound 规则里,0.0.0.0/0被误删了,只留了10.0.0.0/16,而 DMS 实例的私有 IP 不在这个网段。最关键的“第 3 行规则”:在 DMS 控制台创建 Endpoint 时,有一个“Test Connection”按钮。它背后执行的,是 DMS 实例向目标库发起的一次
telnet <host> <port>。如果失败,错误日志永远只显示Connection refused。此时,请立刻登录到 DMS 实例(通过 Session Manager),手动执行:nc -zv <target-host> 3306 # 如果超时,说明网络不通;如果拒绝,说明端口没开或服务没起然后,重点检查安全组的“描述”字段。AWS 控制台里,安全组规则列表的第三行(通常是描述),往往写着
Allow DMS replication instance to connect。如果这个描述是手动添加的,而规则本身没生效(比如协议选错了),你就永远卡在这里。我的经验是:所有安全组规则,必须用 CloudFormation 或 Terraform 管理,杜绝手工修改。
3.2 Oracle 源库配置:ARCHIVELOG 和 Supplemental Logging 的“黄金组合”
DMS 读取 Oracle 日志,依赖两个开关:ARCHIVELOG MODE和SUPPLEMENTAL LOGGING。它们不是“开了就行”,而是有严格的启用顺序和验证方法。
启用 ARCHIVELOG 的完整流程(必须在数据库
MOUNT状态下执行):-- 1. 关闭数据库 SHUTDOWN IMMEDIATE; -- 2. 启动到 MOUNT 状态 STARTUP MOUNT; -- 3. 启用归档模式 ALTER DATABASE ARCHIVELOG; -- 4. 打开数据库 ALTER DATABASE OPEN; -- 5. 验证(必须看到 'ARCHIVELOG') SELECT log_mode FROM v$database;Supplemental Logging 的层级与验证:
- Database-level(必须):
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; - Table-level(针对要迁移的表):
ALTER TABLE dms_sample.sporting_event_ticket ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;
注意:
ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS是最保险的,它会记录所有列的旧值和新值。但代价是归档日志体积增加 15%-20%。如果确定某些表不会被 UPDATE,可以只开PRIMARY KEY级别:ALTER TABLE ... ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY) COLUMNS;。验证是否生效:-- 查看数据库级 SELECT supplemental_log_data_min, supplemental_log_data_pk FROM v$database; -- 查看表级(需指定 owner 和 table_name) SELECT * FROM dba_log_groups WHERE owner='DMS_SAMPLE' AND table_name='SPORTING_EVENT_TICKET';- Database-level(必须):
3.3 LOB(Large Object)处理:32KB 的“甜蜜陷阱”与 Full LOB 模式的代价
DMS 对 LOB 字段(如CLOB,BLOB,TEXT)的处理,是性能瓶颈的重灾区。默认的Limited LOB mode(最大 32KB)看似安全,实则埋雷。
为什么 32KB 是陷阱?Oracle 的
CLOB字段,如果内容超过 32KB,DMS 会把它截断,并在日志里写LOB data truncated to 32768 bytes。业务方发现附件上传失败、富文本内容丢失,第一反应是“DMS 有问题”,其实是配置没调。Full LOB mode 的真实代价:开启
Full LOB mode后,DMS 会把整个 LOB 对象读入内存再传输。这意味着:- 内存爆炸:一个 10MB 的
BLOB,DMS 实例需要至少 20MB 内存来缓存它。如果并发处理 10 个这样的大对象,dms.c5.xlarge(4vCPU, 8GB RAM)实例会 OOM。 - 网络风暴:LOB 数据不经过压缩,直接裸传,会瞬间占满 DMS 实例的网络带宽(
NetworkIn指标飙升)。
- 内存爆炸:一个 10MB 的
我的解决方案:混合模式 + 预处理:
- 识别“真 LOB”:在迁移前,用 SQL 扫描源库,找出
AVG(LENGTH(clob_column)) > 10000的表。这些才是需要Full LOB mode的。 - 对其他 LOB 表,用
Limited LOB mode+Max LOB Size动态调整:例如,把Max LOB Size设为64(KB),覆盖 95% 的场景。 - 终极手段:业务侧改造:对于超大附件(>10MB),说服产品团队,把文件存到 S3,数据库只存 S3 URL。这是成本最低、最稳定的方案。
- 识别“真 LOB”:在迁移前,用 SQL 扫描源库,找出
3.4 外键与触发器:为什么“Drop Foreign Key”是唯一正确的选择
DMS 的 Full Load 不保证表加载顺序。它会并行加载所有表,这就导致:如果orders表有外键指向customers表,而orders表的数据先写入目标库,customers表还没写完,就会触发ERROR: insert or update on table "orders" violates foreign key constraint。
官方推荐的“Do nothing”模式是毒药:它意味着 DMS 会尝试在目标库上重建所有外键。但异构迁移中,源库的
ON DELETE CASCADE在目标库可能不支持,或者约束名长度超限(Oracle 30 字符 vs PostgreSQL 63 字符),导致建约束失败,整个任务卡死。我的标准动作(在 Full Load 前执行):
-- 1. 导出所有外键定义(用于回滚) SELECT 'ALTER TABLE ' || tc.table_name || ' ADD CONSTRAINT ' || tc.constraint_name || ' FOREIGN KEY (' || kcu.column_name || ') REFERENCES ' || ccu.table_name || '(' || ccu.column_name || ');' FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = 'dms_sample'; -- 2. 一键删除所有外键(Aurora MySQL) SELECT CONCAT('ALTER TABLE ', table_name, ' DROP FOREIGN KEY ', constraint_name, ';') FROM information_schema.key_column_usage WHERE constraint_schema = 'dms_sample' AND referenced_table_name IS NOT NULL; -- 3. 删除所有触发器(同样,先备份定义) SHOW TRIGGERS IN dms_sample; DROP TRIGGER IF EXISTS trigger_name;
注意:删除外键后,必须在 Cutover 后,手动在目标库上重建。这不是可选项,是数据治理的底线。重建时,用
ALTER TABLE ... ADD CONSTRAINT ... NOT VALID(PostgreSQL)或ALTER TABLE ... ADD CONSTRAINT ... DISABLE(MySQL 8.0.19+),先建约束但不校验历史数据,等业务稳定后再VALIDATE CONSTRAINT,避免 Cutover 时长不可控。
3.5 CDC Lag 监控:别只盯着“Seconds Behind Source”,要看“Transactions Behind”
CloudWatch 里CDCLag指标显示“120 seconds”,你以为只是慢了 2 分钟?错。这个数字是 DMS 估算的“时间差”,它基于日志中的时间戳。但在高并发场景下,一个事务可能包含 1000 条 SQL,DMS 解析完这 1000 条,才会计为“1 个事务”。所以CDCLag=120s的真实含义,可能是“还有 5000 条未应用的变更”。
真正关键的指标是
TransactionsBehind:它直接告诉你,CDC Agent 还有多少个完整的事务没推送到目标库。这个指标在 DMS 控制台的“Task Details”页的“Table Statistics”里,但默认不显示。你需要:- 在 DMS 控制台,打开你的 Migration Task。
- 点击右上角的 “Actions” → “Edit”。
- 在 “Task Settings” → “Logging” 部分,勾选“Enable detailed transaction logging”。
- 保存后,刷新页面,“Table Statistics” 里就会出现
TransactionsBehind列。
Lag 的健康阈值:对于 OLTP 系统,
TransactionsBehind应该长期保持在< 10。如果持续> 50,说明 CDC Agent 已经严重积压,必须立即干预。干预手段不是重启任务(那会丢数据),而是:- 临时提升 Replication Instance 规格:从
dms.t3.medium升到dms.c5.2xlarge,增加 CPU 和内存,加速日志解析。 - 检查目标库负载:用
SHOW PROCESSLIST(MySQL)或pg_stat_activity(PostgreSQL)看是否有长事务阻塞了 DMS 的写入。 - 启用 Batch-Optimized Apply:在 Task Settings 里勾选此项,DMS 会把多条
INSERT合并为INSERT ... VALUES (...), (...), (...),大幅提升写入吞吐。
- 临时提升 Replication Instance 规格:从
4. 全流程实操:从 CloudFormation 部署到 Cutover 的 7 个关键步骤详解
4.1 CloudFormation 部署:为什么必须亲手写 YAML,而不是用“一键部署”
教程里让你下载一个DMS.yaml文件,点几下就完事。这在 PoC(概念验证)阶段没问题,但在生产环境,这是自杀行为。
模板里的“硬编码”是定时炸弹:
DMS.yaml里必然有类似InstanceType: dms.c5.xlarge的行。生产环境的规格,必须根据源库大小、QPS、LOB 比例来计算。一个 500GB 的 Oracle 库,用dms.c5.xlarge可能跑得飞快;但一个 2TB 的 SQL Server 库,同样的规格会让 CDC Lag 稳定在 3 小时以上。我的生产级 CFN 模板结构:
Parameters: ReplicationInstanceClass: Type: String Default: dms.c5.2xlarge AllowedValues: [dms.t3.medium, dms.c5.xlarge, dms.c5.2xlarge, dms.c5.4xlarge] Description: "Choose the DMS Replication Instance class based on your workload" SourceDBEngine: Type: String Default: oracle AllowedValues: [oracle, mysql, postgres, sqlserver] TargetDBEngine: Type: String Default: aurora-mysql AllowedValues: [aurora-mysql, aurora-postgresql, redshift, dynamodb] Resources: DMSReplicationInstance: Type: AWS::DMS::ReplicationInstance Properties: ReplicationInstanceClass: !Ref ReplicationInstanceClass # 其他属性...这样,每次部署,你都能在 CloudFormation 控制台的“Parameters”页,根据当前项目的真实需求,选择最合适的规格,而不是被一个静态模板绑架。
4.2 SCT Schema Conversion:从“Green/Blue/Red”到“可交付物”的最后一公里
SCT 的界面很炫,但它的输出目录结构,是新人最容易迷失的地方。一个典型的AWS Schema Conversion Tool项目,生成的文件夹如下:
C:\Users\Administrator\AWS Schema Conversion Tool\ ├── Projects\ │ └── AWS Schema Conversion Tool Source DB to Aurora MySQL\ │ ├── AssessmentReport.html # 评估报告(只看,不执行) │ ├── Converted\ # SCT 自动生成的“目标库”DDL │ │ └── dms_sample\ │ │ ├── Tables\ │ │ │ └── sporting_event_ticket.sql │ │ └── Views\ │ │ └── v_customer_summary.sql │ └── ManualConversion\ # 你需要手动写的“补丁” │ └── dms_sample\ │ └── Procedures\ │ └── p_calculate_revenue.sql # 这里放你重写的 PL/pgSQL执行顺序铁律:
- 先运行
Converted\下的所有.sql文件:用 MySQL Workbench 或mysql命令行,按文件名字母序执行(SCT 保证了依赖顺序)。 - 再运行
ManualConversion\下的文件:这些是你和业务方对齐后,手动编写的逻辑等价代码。 - 最后,执行
Converted\下的Constraints\文件夹(如果存在):这是 SCT 生成的外键和索引。但记住,我们前面已经决定“先删后建”,所以这里只建索引,不建外键。
- 先运行
一个必做的“消毒”步骤:SCT 生成的 SQL 文件,开头通常有
SET FOREIGN_KEY_CHECKS=0;和SET SQL_MODE='STRICT_TRANS_TABLES';。在生产环境,STRICT_TRANS_TABLES会导致很多隐式转换失败(比如字符串'123'插入整型字段)。我的做法是:用 VS Code 的批量替换,把所有SET SQL_MODE=替换为-- SET SQL_MODE=,注释掉它。
4.3 DMS Task 创建:Table Mapping 与 Transformation Rules 的“精准外科手术”
DMS 的 Table Mapping Wizard 很方便,但它生成的规则是“粗粒度”的。Schema: DMS_SAMPLE%会匹配所有以DMS_SAMPLE开头的 Schema,但如果源库有DMS_SAMPLE_ARCHIVE这个历史归档库,它也会被拉进来,白白消耗资源。
我的精细化 Mapping 规则(JSON 格式,比 Wizard 更可控):
{ "rules": [ { "rule-type": "selection", "rule-id": "1", "rule-name": "Select only main schema", "object-locator": { "schema-name": "DMS_SAMPLE", "table-name": "%" }, "rule-action": "include" }, { "rule-type": "selection", "rule-id": "2", "rule-name": "Exclude archive tables", "object-locator": { "schema-name": "DMS_SAMPLE", "table-name": "%_archive" }, "rule-action": "exclude" } ] }这样,
DMS_SAMPLE_ARCHIVE表被精准排除,DMS_SAMPLE_ORDERS表被精准包含。Transformation Rules 的真实用途:教程里让你把 Schema/Table/Column 全部转小写,这只是冰山一角。它真正的威力在于数据清洗。例如,源库的
user_email字段,可能有NULL、空字符串、' '(空格)、'test@domain.com '(尾部空格)。在 Mapping 规则里加一条:{ "rule-type": "transformation", "rule-id": "3", "rule-name": "Trim and lower email", "rule-action": "transform", "rule-target": "column", "object-locator": { "schema-name": "DMS_SAMPLE", "table-name": "users", "column-name": "user_email" }, "value": "TRIM(LOWER(user_email))" }这样,DMS 在迁移时,会自动执行
TRIM(LOWER()),无需在源库或目标库额外写 ETL 脚本。
4.4 Full Load 监控:如何从“100%”进度条里,看出数据倾斜的苗头
DMS 控制台的“Table Statistics”页,显示每个表的Full Load Progress。当它显示100%,你以为完事了?不,这只是“数据已发出”,不等于“数据已写入”。
- 必须交叉验证的三个指标:
Rows inserted:在目标库上,用SELECT COUNT(*) FROM dms_sample.orders;查看实际行数。如果比源库少,说明有主键冲突或约束拦截。Rows skipped:这个数字必须为0。如果大于0,说明 DMS 遇到了无法处理的行(比如 LOB 截断、字符集不兼容),它会跳过并记日志。必须查日志定位。Full Load Throughput (MB/s):这个指标应该是一条平稳上升的曲线。如果它在某个表(比如orders)上突然暴跌到0.1 MB/s,而其他表正常,说明orders表有热点(Hotspot)——比如所有新订单都写入id最大的那个分区,DMS 的并行连接都在争抢同一片数据页。解决方案是:在源库上,对orders表的主键id,执行ALTER TABLE orders ORDER BY id;(MySQL)或CLUSTER orders USING orders_pkey;(PostgreSQL),物理重排数据。
4.5 CDC 追平与 Cutover:一场精确到秒的“外科手术”
CDC 追平(Catch-up)不是等CDCLag=0就完事。它是一个需要精确计算的窗口期。
追平的数学公式:
Cutover Start Time = Current Time - CDCLag (in seconds) - Safety Margin (300 seconds)例如,现在是
14:00:00,CDCLag=120s,那么你应该在13:57:40开始 Cutover 流程。这 300 秒(5 分钟)的安全边际,是留给网络抖动、应用缓存失效、DNS 刷新的时间。Cutover 的原子化 Checklist(必须按顺序执行,缺一不可):
- Step 0(T-5min):在源库执行
FLUSH LOGS;(MySQL)或ALTER SYSTEM SWITCH LOGFILE;(Oracle),强制产生一个新的日志文件,作为 CDC 的“终点锚点”。 - Step 1(T-2min):在源库,将所有业务应用的数据库连接池,设置为
READ ONLY。验证:SELECT @@read_only;(MySQL)返回ON。 - Step 2(T-1min):在 DMS 控制台,点击
Stop Task。等待状态变为Stopping→Stopped。注意:不要点“Delete”,只 Stop。 - Step 3(T=0):在目标库,执行
SET GLOBAL read_only=OFF;(MySQL)或ALTER DATABASE dms_sample SET ALLOW_CONNECTIONS = ON;(PostgreSQL),解除只读。 - Step 4(T+30s):将所有应用的数据库连接字符串,切换到目标库地址。必须用 DNS 切换,而不是改配置文件!DNS TTL 设为
30秒,确保 30 秒内全球生效。 - Step 5(T+2min):在目标库,执行
SELECT COUNT(*) FROM dms_sample.orders WHERE created_at > '2023-10-01 13:57:40';,确认最新数据已写入。 - Step 6(T+5min):在源库,执行
SHOW MASTER STATUS;(MySQL)或SELECT NAME, FIRST_TIME, NEXT_TIME FROM V$ARCHIVED_LOG WHERE DEST_ID=1 ORDER BY FIRST_TIME DESC FETCH FIRST 1 ROWS ONLY;(Oracle),记录最后一个日志位置。这是回滚的“救命稻草”。
- Step 0(T-5min):在源库执行
提示:整个 Cutover 过程,必须用一个共享的在线文档(如 Google Doc)实时记录每一步的执行时间、命令、返回结果。这是事后复盘的唯一依据。
4.6 Post-Migration 验证:超越“行数一致”的深度校验
DMS 的数据验证(Data Validation)功能,只能告诉你“行数和校验和一致”。但业务的核心诉求是:“钱没少,单没丢,用户能登录”。这需要更细粒度的验证。
- 我的三级验证法:
- Level 1:核心业务表抽样(10 分钟):用
pt-table-checksum对orders,users,accounts三张表,按id % 1000 = 0抽样 1000 行,比对所有字段。 - Level 2:关键业务逻辑验证(30 分钟):写一个 Python 脚本,模拟真实业务场景。例如:
# 场景:用户下单,检查订单金额、库存扣减、积分增加是否同步 source_order = get_from_source("SELECT amount, item_id, qty FROM orders WHERE id = 12345") target_order = get_from_target("SELECT amount, item_id, qty FROM orders WHERE id = 12345") assert source_order == target_order, "Order mismatch!" # 检查库存 source_stock = get_from_source("SELECT stock FROM inventory WHERE item_id = 1001") target_stock = get_from_target("SELECT stock FROM inventory WHERE item_id = 1001") assert abs(source_stock - target_stock) < 1, "Stock mismatch!" - Level 3:全量数据一致性扫描(可选,2 小时+):用 AWS Glue Job,读取源库和目标库的全量数据,用 Spark 的
exceptAll()方法,找出所有差异行。这步只在金融、支付等强一致性要求的场景执行。
- Level 1:核心业务表抽样(10 分钟):用
4.7 Cleanup:为什么“删 Stack”不是终点,而是新监控的起点
CloudFormation Stack 删除后,你以为万事大吉?不,这是监控告警的开始。
- 必须保留的三类日志:
- DMS Task Logs:在 CloudWatch Logs 里,找到
/aws/dms/<task-name>日志组。即使任务已删,这些日志默认保留 30 天。把ERROR和WARN级别的日志导出为 CSV,用 Excel 分析高频错误码
- DMS Task Logs:在 CloudWatch Logs 里,找到
