AWS DMS实战手记:低风险数据库迁移的分层可控模型

📅 2026/7/7 21:51:43
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 ToolSCT。而 SCT 的输出从来不是“开箱即用”的。我经手的一个 Oracle→Aurora PostgreSQL 迁移SCT 自动生成了 237 个对象其中189 个表GreenDDL 创建成功但 32 个表的TIMESTAMP WITH TIME ZONE字段被强制转为TIMESTAMP WITHOUT TIME ZONE业务方反馈时区计算全乱41 个视图BlueSCT 生成了基础CREATE VIEW但原 Oracle 视图里嵌套了DBMS_LOB.GETLENGTH()函数SCT 直接报错需要手动改写为 PostgreSQL 的LENGTH()7 个存储过程RedSCT 完全无法转换提示PL/SQL block not supported最终由 DBA 用 PL/pgSQL 重写了 3 天。注意不要迷信 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 TABLEMySQL或VACUUM ANALYZEPostgreSQL让查询优化器更新统计信息避免 CDC 期间因执行计划劣化导致的慢 SQL。必须有一套独立于 DMS 的“数据一致性快照”机制DMS 自带的数据验证Data Validation功能只校验行数和校验和Checksum它无法发现“源库某条记录的amount是 100.00目标库写成了 100.0000000001”这种精度漂移。因此我在每个关键节点Full Load 结束、CDC 追平、Cutover 前都会用pt-table-checksumPercona 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-sgInbound 规则只允许来自源库 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 ownerDMS_SAMPLE AND table_nameSPORTING_EVENT_TICKET;3.3 LOBLarge Object处理32KB 的“甜蜜陷阱”与 Full LOB 模式的代价DMS 对 LOB 字段如CLOB,BLOB,TEXT的处理是性能瓶颈的重灾区。默认的Limited LOB mode最大 32KB看似安全实则埋雷。为什么 32KB 是陷阱Oracle 的CLOB字段如果内容超过 32KBDMS 会把它截断并在日志里写LOB data truncated to 32768 bytes。业务方发现附件上传失败、富文本内容丢失第一反应是“DMS 有问题”其实是配置没调。Full LOB mode 的真实代价开启Full LOB mode后DMS 会把整个 LOB 对象读入内存再传输。这意味着内存爆炸一个 10MB 的BLOBDMS 实例需要至少 20MB 内存来缓存它。如果并发处理 10 个这样的大对象dms.c5.xlarge4vCPU, 8GB RAM实例会 OOM。网络风暴LOB 数据不经过压缩直接裸传会瞬间占满 DMS 实例的网络带宽NetworkIn指标飙升。我的解决方案混合模式 预处理识别“真 LOB”在迁移前用 SQL 扫描源库找出AVG(LENGTH(clob_column)) 10000的表。这些才是需要Full LOB mode的。对其他 LOB 表用Limited LOB modeMax LOB Size动态调整例如把Max LOB Size设为64KB覆盖 95% 的场景。终极手段业务侧改造对于超大附件10MB说服产品团队把文件存到 S3数据库只存 S3 URL。这是成本最低、最稳定的方案。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 VALIDPostgreSQL或ALTER TABLE ... ADD CONSTRAINT ... DISABLEMySQL 8.0.19先建约束但不校验历史数据等业务稳定后再VALIDATE CONSTRAINT避免 Cutover 时长不可控。3.5 CDC Lag 监控别只盯着“Seconds Behind Source”要看“Transactions Behind”CloudWatch 里CDCLag指标显示“120 seconds”你以为只是慢了 2 分钟错。这个数字是 DMS 估算的“时间差”它基于日志中的时间戳。但在高并发场景下一个事务可能包含 1000 条 SQLDMS 解析完这 1000 条才会计为“1 个事务”。所以CDCLag120s的真实含义可能是“还有 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 PROCESSLISTMySQL或pg_stat_activityPostgreSQL看是否有长事务阻塞了 DMS 的写入。启用 Batch-Optimized Apply在 Task Settings 里勾选此项DMS 会把多条INSERT合并为INSERT ... VALUES (...), (...), (...)大幅提升写入吞吐。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_CHECKS0;和SET SQL_MODESTRICT_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、空字符串、 空格、testdomain.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不是等CDCLag0就完事。它是一个需要精确计算的窗口期。追平的数学公式Cutover Start Time Current Time - CDCLag (in seconds) - Safety Margin (300 seconds)例如现在是14:00:00CDCLag120s那么你应该在13:57:40开始 Cutover 流程。这 300 秒5 分钟的安全边际是留给网络抖动、应用缓存失效、DNS 刷新的时间。Cutover 的原子化 Checklist必须按顺序执行缺一不可Step 0T-5min在源库执行FLUSH LOGS;MySQL或ALTER SYSTEM SWITCH LOGFILE;Oracle强制产生一个新的日志文件作为 CDC 的“终点锚点”。Step 1T-2min在源库将所有业务应用的数据库连接池设置为READ ONLY。验证SELECT read_only;MySQL返回ON。Step 2T-1min在 DMS 控制台点击Stop Task。等待状态变为Stopping→Stopped。注意不要点“Delete”只 Stop。Step 3T0在目标库执行SET GLOBAL read_onlyOFF;MySQL或ALTER DATABASE dms_sample SET ALLOW_CONNECTIONS ON;PostgreSQL解除只读。Step 4T30s将所有应用的数据库连接字符串切换到目标库地址。必须用 DNS 切换而不是改配置文件DNS TTL 设为30秒确保 30 秒内全球生效。Step 5T2min在目标库执行SELECT COUNT(*) FROM dms_sample.orders WHERE created_at 2023-10-01 13:57:40;确认最新数据已写入。Step 6T5min在源库执行SHOW MASTER STATUS;MySQL或SELECT NAME, FIRST_TIME, NEXT_TIME FROM V$ARCHIVED_LOG WHERE DEST_ID1 ORDER BY FIRST_TIME DESC FETCH FIRST 1 ROWS ONLY;Oracle记录最后一个日志位置。这是回滚的“救命稻草”。提示整个 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()方法找出所有差异行。这步只在金融、支付等强一致性要求的场景执行。4.7 Cleanup为什么“删 Stack”不是终点而是新监控的起点CloudFormation Stack 删除后你以为万事大吉不这是监控告警的开始。必须保留的三类日志DMS Task Logs在 CloudWatch Logs 里找到/aws/dms/task-name日志组。即使任务已删这些日志默认保留 30 天。把ERROR和WARN级别的日志导出为 CSV用 Excel 分析高频错误码