注册
Oracle到DM8动态迁移与回退(基于DRS)
专栏/技术分享/ 文章详情 /

Oracle到DM8动态迁移与回退(基于DRS)

悬铃木 2026/08/21 188 1 0
摘要

Oracle→DM8 动态迁移与回退(基于 DRS)

本文方案如下:

Oracle 持续写入期间,用达梦 DRS 完成全量与增量同步后割接至 DM8;DM8 运行期的新事务经反向 DRS 回传 Oracle;一段时间后回退到 Oracle。

1. 背景与目标

生产系统从 Oracle 迁移至 DM8 时,主要目标是缩短停机窗口:迁移期间源库继续写入,同步工具先完成全量装载,再从一个一致点继续增量;割接窗口内停止源库写入、追平、校验,切换业务入口。

本文目标:

  • Oracle 持续写入时,全量装载与增量连续衔接;
  • 割接动作:停写、追平、校验、序列推进、约束与触发器恢复;
  • DM8 接管后新事务经反向链路回传 Oracle;
  • 回退 Oracle 后原业务入口恢复写入;
  • Oracle 与 DM 自定义类型表通过影子表完成正向、反向的全量与增量。

约束:任意时刻只允许一个数据库写入业务。

2. 环境与拓扑

项目 Oracle 源端 DM8 目标端
产品 Oracle AI Database 26ai Free 23.26.2.0.0 DM8(Oracle 兼容模式,COMPATIBLE_MODE=2
部署 KylinSP3 Linux 虚拟机 Docker 容器 oracle-free-26ai Windows 本机实例 DM8ORA
地址 192.168.79.143:1521/FREEPDB1 127.0.0.1:5237
业务模式 MIG_APPMIG_AUDITMIG_RPT 同名兼容结构
同步技术模式 MIG_SYNC MIG_SYNC
字符集 AL32UTF8 UTF-8

Oracle 端日志要求:

LOG_MODE = ARCHIVELOG
SUPPLEMENTAL_LOG_DATA_MIN = YES
SUPPLEMENTAL_LOG_DATA_ALL = YES
ENABLE_GOLDENGATE_REPLICATION = TRUE

DRS V5.2.5.4 配置两条链路:

链路 进程 端口 用途
正向 Oracle→DM Linux CPT → Windows EXEC Windows 5345 全量装载与增量同步
反向 DM→Oracle Windows CPT → Linux EXEC Linux 5346 回传 DM 新事务

两端均使用 DRS 专用账户 DMDRS。流程图如下:

flowchart LR
    APP["业务写入(可切换)"]
    ORA["Oracle FREEPDB1<br>192.168.79.143:1521"]
    FCPT["正向 CPT(Linux)"]
    FEXEC["正向 EXEC(Windows)"]
    DM["DM8ORA<br>127.0.0.1:5237"]
    RCPT["反向 CPT(Windows)"]
    REXEC["反向 EXEC(Linux)"]

    APP -->|"正向阶段写 Oracle"| ORA
    ORA --> FCPT -->|"5345"| FEXEC --> DM
    APP -.->|"割接后写 DM"| DM
    DM -.-> RCPT -.->|"5346"| REXEC -.-> ORA

业务范围共 32 张表。MIG_APP.MIG_BIG_SALES_LINE 保留现有 10,100,000 行,不重新全量;其余业务表按接收态清空并重新装载初始全量数据,DM 端已验证的兼容结构与程序对象全部保留。另建 5 张影子表,用于复制 4 张自定义类型业务表;影子表是同步基础设施,不计入 32 张业务表。

同步范围使用显式白名单,排除以下对象:

  • Oracle Text 内部表 DR$PRODUCT_MANUAL_CTX_IX$*
  • DMFLDR_*TEST_**_PROBE*_BENCH 等测试表;
  • DM 物化视图内部表 MTAB$*

3. 方案设计

3.1 四个阶段

image.png

  • 第一阶段:Oracle 持续写入;普通表由 DRS 直接同步,自定义类型表先在 Oracle 事务内展开到影子表,再由 DRS 同步影子表;
  • 第二阶段:停止 Oracle 写入,正向链路追平,校验通过后只写 DM8;
  • 第三阶段:DM8 产生新事务,反向链路回传 Oracle;
  • 第四阶段:停止 DM 写入,反向链路追平,校验通过后只写 Oracle。

3.2 目标端两种状态

DM8 目标结构分为两个可重复状态:

状态 内容 使用阶段
接收态 表结构完整;主键、唯一键等用于增量行定位的索引保留;普通业务触发器、外键关闭;LOB 非空约束临时放宽;影子表“业务表→影子表”生产组件禁用;全量批量期间先关闭“影子表→业务表”接收组件并用批处理重建,切入增量前再启用接收组件 DRS 全量与增量
生产态 约束与索引完整;业务触发器启用;业务序列校准;影子表方向切换为“业务表→影子表”生产组件启用,“影子表→业务表”应用组件禁用 DM 开放写入前

接收态:禁用 6 个触发器、20 个业务外键;4 个 LOB 列临时允许 NULL;

3.3 对象分类与处理

类别 处理方式
普通表 DRS 全量装载与增量同步
LOB 表 DRS 两阶段写入,接收期放宽 NOT NULL,割接前校验后恢复
VPD/RLS 表 对迁移账户授予 EXEMPT ACCESS POLICY,保证读取完整源数据
自定义类型表 原表不直接进入 DRS;完整行拆成 5 张普通影子表,源端事务内维护,目标端由接收组件重建原类型;正反向均由 DRS 同步影子表
序列 不随表数据复制;未启用序列同步时,割接/回退前手动推进
约束与触发器 接收期禁用,割接/回退恢复;DDL 审计触发器保持禁用

3.4 全量装载的一致点

需要重新全量装载的普通表与影子表均逐表使用 INSERT|FLASHBACK。每张表按 FLASHBACK 的一致点读取,全量完成后从该点继续增量,覆盖全量读取期间产生的新事务。千万行大表已确认现有数据完整,不参与本轮全量装载。影子表必须在 DRS 建链前与源业务表校验一致,之后由同一业务事务持续维护;否则即使全量结果一致,复制的也只是错误存量数据。

4. 正向迁移与割接

4.1 实时流量模拟

Oracle 端运行持续写入脚本 oracle_realtime_workload.sql,覆盖下单、订单明细、支付、状态变更、业务事件与审计写入,同时覆盖主从表、事务提交顺序与 LOB。脚本在虚拟机后台启动:

cd /drs/work/oracle_to_dm/source : > oracle_workload.log nohup sh -c 'sudo docker exec -i oracle-free-26ai sqlplus -s / as sysdba \ < /drs/work/oracle_to_dm/source/oracle_realtime_workload.sql' \ > oracle_workload.log 2>&1 < /dev/null & echo $! > oracle_workload.pid

脚本:(循环调用业务包,每轮一笔完整订单事务,交替提交与取消):

-- oracle_realtime_workload.sql for i in 1..7200 loop pkg_order_api.create_order( 1, 1003, 11, case mod(i,4) when 0 then 'APP' when 1 then 'WEB' when 2 then 'STORE' else 'PARTNER' end, order_line_nt( order_line_ot(10003, 1 + mod(i,3), null), order_line_ot(10004, 1 + mod(i,2), null) ), trunc(sysdate), systimestamp, l_order_id ); if mod(i,5) = 0 then pkg_order_api.cancel_order(l_order_id, 'DRS workload cancellation'); else pkg_order_api.submit_order(l_order_id); pkg_order_api.capture_payment( l_order_id, 'DRS-PAY-' || l_order_id, 'WECHAT', systimestamp ); end if; commit; dbms_session.sleep(1); end loop;

上面的现有脚本只覆盖普通订单链路。实施影子表方案时,还要增加一条 UDT 业务流,直接通过正常业务入口修改电话号码数组与三类地址对象,并与订单脚本同时运行;

4.2 全量装载与资源控制

现有演练首次把 31 张表一次性以 INSERT|GROUP|FLASHBACK|SAME 加入全量。Windows 目标端剩余内存约 1 GB,DRS 内存用到约 2 GB 后主动停止。原因是源端分组读取与目标端并行接收超出测试机可用内存。

目标端配置调整为(两个线程参数取最小值 1;装载缓冲从 1024 MB 修改到 128 MB):

<load_thr>1</load_thr> <load_buf_limit>128</load_buf_limit> <work_thr>1</work_thr>

随后改为表级串行,逐表提交并等待完成:

alter cpt_oracle_drsuser add table 
  "sch.name='MIG_APP' and tab.name='APP_CONFIG'" INSERT|FLASHBACK

串行脚本为 run_forward_serial.sh。Oracle 分区表即使不显式指定 GROUP,DRS 仍可能按分区自动分组,因此资源控制主要靠表级串行,再加上目标端装载线程和缓存限额。

脚本主要逻辑(逐表提交 add table,确认该表装载完成后再提交下一张):

# run_forward_serial.sh 主要逻辑 while read -r schema_name table_name; do before=$(grep -c '所有表都已经装载结束,全部成功' "$log_file" || true) printf "alter cpt_oracle_drsuser add table \"sch.name='%s' and tab.name='%s'\" INSERT|FLASHBACK\n" \ "$schema_name" "$table_name" >> "$input_file" deadline=$((SECONDS + 900)) while true; do after=$(grep -c '所有表都已经装载结束,全部成功' "$log_file" || true) (( after > before )) && break # 计数增加,确认当前表装载完成 pgrep -f 'drsvr.*cpt_oracle.xml' >/dev/null || exit 3 (( SECONDS >= deadline )) && exit 4 # CPT 退出或超时则终止 sleep 2 done done

4.3 特殊对象处理

VPD/RLS 表。 迁移账户 DMDRS 受源库 VPD 策略影响,部分表查询结果为零行或不完整。为使迁移账户读取与业务策略无关的完整源数据:

GRANT EXEMPT ACCESS POLICY TO DMDRS;

该权限风险较高,仅用于受控迁移账户,生产应同时限制账户登录来源、密码托管与生命周期。

LOB 两阶段写入。 本版本 DRS 对 LOB 先插入普通列、再回填 LOB。若目标列保持 NOT NULL,第一阶段临时空值会被拒绝。接收期放宽四列:

ALTER TABLE MIG_APP.APP_CONFIG MODIFY CONFIG_JSON NULL; ALTER TABLE MIG_APP.BUSINESS_EVENT MODIFY PAYLOAD_JSON NULL; ALTER TABLE MIG_APP.CUSTOMER_PRIVATE_NOTE MODIFY NOTE_TEXT NULL; ALTER TABLE MIG_APP.PROMOTION MODIFY RULE_JSON NULL;

割接前校验空值为 0 后恢复非空约束,这只是临时放宽。

VARRAY/OBJECT 类型。 当前 DRS 版本不能可靠完成下列 Oracle/DM 自定义类型的异构绑定;DM 作为反向日志源时,甚至会在装载整表字典阶段因自定义类型失败:

  • MIG_APP.CUSTOMER.PHONESPHONE_VARRAY_T
  • MIG_APP.CUSTOMER_ADDRESS.ADDRESS_VALUEADDRESS_OT
  • MIG_APP.SHIPMENT.DELIVERY_ADDRADDRESS_OT
  • MIG_APP.WAREHOUSE.WAREHOUSE_ADDRADDRESS_OT

Oracle 端补充类型 EXECUTE 权限只能让 DRS 读取类型字典,不能保证目标驱动可以绑定复杂值。DRS 虽提供 CVT 数据清洗转换,但 V5.2.5.4 配套手册明确将数据库自定义类型排除在 CVT 支持的数据类型之外。

4.3.1 影子表的设计原则

为 4 张自定义类型业务表建立 5 张不含任何 UDT、LOB 或数据库专有对象的普通关系表:

影子表 内容 主键
MIG_SYNC.CUSTOMER_FLAT CUSTOMERPHONES 外的全部普通列 CUSTOMER_ID
MIG_SYNC.CUSTOMER_PHONE_FLAT 一个电话号码一行,包含 CUSTOMER_IDPHONE_SEQPHONE_NO (CUSTOMER_ID, PHONE_SEQ)
MIG_SYNC.CUSTOMER_ADDRESS_FLAT 地址表全部普通列,并把 ADDRESS_VALUE 展开为省、市、区、街道、邮编 ADDRESS_ID
MIG_SYNC.SHIPMENT_FLAT 发货表全部普通列,并把 DELIVERY_ADDR 展开为 5 个标量属性 SHIPMENT_ID
MIG_SYNC.WAREHOUSE_FLAT 仓库表全部普通列,并把 WAREHOUSE_ADDR 展开为 5 个标量属性 WAREHOUSE_ID

影子表保存业务表的完整可复制语义,不只保存复杂列。这样一行数据不会拆成“普通列由 DRS 同步、复杂列由另一条链路同步”,反向链路也不会丢失普通列的变化。

MIG_SYNC 应归独立账户所有,不能让高权限迁移账户 DMDRS 成为影子表所有者。两端影子表必须同名、同列序、同标量类型,并为 DRS 行定位建立主键或唯一键。权限按方向最小化授予:正向时 Oracle 的 DMDRS 对源影子表具有 SELECT,DM 的 DMDRS 对目标影子表具有 INSERT/UPDATE/DELETE;反向时权限方向相反。源端还必须按相应数据库和 DRS 手册要求为影子表启用可捕获的主键/唯一键日志及补充日志。切换方向时只调整必要权限,不把影子表开放给业务账户直接写入。
image.png

其中 PHONE_SEQ 必须稳定保存 VARRAY 的顺序。更新电话号码数组时,生产组件可在同一事务中删除该客户原明细,再按数组下标重新插入;事务提交前,影子表不能停留在中间状态。

4.3.2 双端组件与角色切换

Oracle 和 DM 两端各部署两类数据库内组件:

  1. 生产组件(业务表→影子表):业务对 UDT 表执行 INSERT/UPDATE/D ELETE 时,在同一数据库事务中展开并维护影子表;任何影子维护失败都必须使业务事务一起回滚,禁止组件内部独立 COMMIT
  2. 接收组件(影子表→业务表):DRS 将普通影子表 DML 写入接收端后,按主键执行幂等 MERGE/DELETE,使用 PHONE_VARRAY_T(...)ADDRESS_OT(...) 重建目标业务表。

为避免“业务表→影子表→DRS→业务表→影子表”回环,同一数据库同一时刻只启用一个方向:

数据库角色 生产组件 接收组件
当前业务主库 启用 禁用
当前 DRS 接收库 禁用 启用

组件开关应由专用配置表或会话上下文判断,不能靠人工临时删除触发器。普通业务触发器在接收期仍然禁用;影子表接收组件是复制基础设施,须单独管理。

4.3.3 正向全量与增量

正向阶段不把上述 4 张 UDT 原表加入 DRS 白名单,只加入 5 张普通影子表。实施顺序为:

  1. 在迁移前置阶段创建 Oracle 影子表和生产组件;
  2. 对现有 UDT 数据做一次回填。
  3. 核对源业务表与源影子表的行数、主键集合、空值、电话顺序和地址属性;
  4. 从此由 Oracle 业务事务持续维护影子表;
  5. DRS 对 27 张需要重新全量的普通业务表和 5 张影子表执行 INSERT|FLASHBACK;千万行大表保留现有数据并记录增量起点;全量期间 Oracle 的新事务继续产生日志,由增量捕获;
  6. DM 全量装载期间先关闭影子接收组件。5 张影子表追到选定检查点后,短暂暂停目标端应用(源端仍可继续写并积压日志),运行一次批处理,从影子表重建 4 张 DM UDT 业务表;批处理校验通过后启用“影子表→UDT业务表”增量接收组件,再恢复目标端应用;
  7. 恢复后检查:检查点之后的积压事务是否全部经过接收组件;跨 CUSTOMER_FLATCUSTOMER_PHONE_FLAT 的同一源事务在 DM 是否仍保持为一个目标事务;
  8. 实时流量必须包含电话号码、客户地址、发货地址和仓库地址的新增、修改、删除,不能靠“不修改特殊表”跳过验证。

全量与增量都只经过同一条 DRS 影子表链路。

image.png

4.3.4 影子表专项检查

割接前除普通表校验外,必须完成以下检查:

  • 5 张影子表两端行数与主键集合一致;
  • Oracle UDT 属性、Oracle 影子标量、DM 影子标量、DM UDT 属性四份语义一致;
  • 专门执行仅修改复杂列的事务,确认目标端发生变化;
  • 专门执行同一事务同时修改普通列和复杂列,确认目标端整行一致;
  • 验证插入、更新、删除、事务回滚与 DRS 重放幂等;
  • 验证组件异常时业务/接收事务整体失败,不产生“影子成功、业务表失败”或相反状态。

44 同步校验与 LOB 定向修复

持续写入期间,普通关系表链路的校验结果:

Oracle 行数 DM 行数 最大业务键
SALES_ORDER 22,732 22,732 1009607
SALES_ORDER_ITEM 65,463 65,463 ORDER_ID=1009607
ORDER_STATUS_HISTORY 24,918 24,918 HISTORY_ID=2013093
PAYMENT 16,185 16,185 PAYMENT_ID=1511485
BUSINESS_EVENT 207,650 207,650 EVENT_ID=20051701
CHANGE_AUDIT 9,296 9,296 AUDIT_ID=22066
INVENTORY_TXN 4,376 4,376 TXN_ID=1018971

LOB 完整性检查与定向修复。 增量追平后,BUSINESS_EVENT 行数与最大 EVENT_ID 相等,但校验发现 DM 最新增量区间有 588 行 PAYLOAD_JSON IS NULL,Oracle 对应值全部非空。

使用 CPT 的 LOB 定向修复:

cp cpt_oracle_drsuser \
  "sch.name='MIG_APP' and tab.name='BUSINESS_EVENT'" \
  UPDATE|LOB|WHERE|"EVENT_ID>=20051114 AND EVENT_ID<=20051701"

修复后两端空值数均为 0,随后恢复 PAYLOAD_JSON NOT NULL。LOB 校验至少包含空值数、长度和抽样内容。

4.6 割接

顺序为:停止 Oracle 写入 → 正向链路保持运行追平 → 普通表、影子表和两端 UDT 语义全部校验通过 → 先停 CPT、再停 EXEC → 切换两端影子组件角色 → DM 开始接受写入。用 DRS 控制台命令停止,不要直接杀进程:

stop cpt_oracle_drsuser
stop exec_dm8_drsuser

影子组件角色切换。 正向链路完全停止后执行以下状态转换:

数据库 切换前 切换后
Oracle 业务表→影子表启用;影子表→业务表禁用 两者暂时均禁用,待反向链路建立前启用影子表→业务表
DM 影子表→业务表启用;业务表→影子表禁用 影子表→业务表禁用;业务表→影子表启用

角色切换必须在业务停写窗口完成,并检查配置表和触发器/过程的实际状态。两端生产组件不能同时启用,否则会形成双向回环。

序列推进。 DRS 回放业务数据时应用显式主键值。本环境未启用序列同步(EXEC 参数 seq_sync_mode 默认 0,序列同步操作只保存不应用),DM 序列如果不推进,首次业务写入可能取到与迁移数据冲突的旧值:

ALTER SEQUENCE MIG_APP.SEQ_ORDER_ID CURRENT VALUE 1010000; ALTER SEQUENCE MIG_APP.SEQ_EVENT_ID CURRENT VALUE 20055000; ALTER SEQUENCE MIG_APP.SEQ_HISTORY_ID CURRENT VALUE 2014000; ALTER SEQUENCE MIG_APP.SEQ_INV_TXN_ID CURRENT VALUE 1019000; ALTER SEQUENCE MIG_APP.SEQ_PAYMENT_ID CURRENT VALUE 1512000; ALTER SEQUENCE MIG_APP.SEQ_SHIPMENT_ID CURRENT VALUE 1604000; ALTER SEQUENCE MIG_AUDIT.SEQ_AUDIT_ID CURRENT VALUE 23001; ALTER SEQUENCE MIG_AUDIT.SEQ_VALIDATION_ID CURRENT VALUE 501;

取值原则:高于当前最大键并按缓存大小取整的安全上界。序列允许空洞,不允许倒退碰撞。

约束与触发器恢复。 4 个 LOB 列空值校验为 0 后恢复 NOT NULL;恢复 20 个业务外键;启用 5 个业务触发器;TRG_DDL_AUDIT 迁移前即禁用,保持禁用。

5. 反向同步与回退

5.1 DM 日志源配置

反向链路需要 DM 提供归档与逻辑日志。修改 D:\DM8\DM8SERVER\data\DM8ORA\dm.ini

ARCH_INI = 1 RLOG_APPEND_LOGIC = 1 RLOG_APPEND_SYSTAB_LOGIC = 1

新建 dmarch.ini

[ARCHIVE_LOCAL1] ARCH_TYPE = LOCAL ARCH_DEST = D:\DM8\DM8SERVER\data\DM8ORA\arch ARCH_FILE_SIZE = 128 ARCH_SPACE_LIMIT = 10240

静态参数修改后必须重启 DM。

重启后以视图与端口为准验证:

SELECT PARA_NAME, PARA_VALUE, FILE_VALUE FROM V$DM_INI WHERE PARA_NAME IN ('ARCH_INI','RLOG_APPEND_LOGIC','RLOG_APPEND_SYSTAB_LOGIC');

实测三个参数内存值与文件值均为 1,本地归档目录已产生首个 128 MB 归档文件。

5.2 反向链路搭建

Oracle 接收态。 禁用 6 个普通业务触发器、18 个普通外键;4 个 LOB 非空列临时允许 NULL;为 Oracle 端 DMDRS 逐表授予 DML。影子表组件单独切换为“业务表→影子表禁用、影子表→业务表启用”,不计入这里禁用的普通业务触发器。

两条引用分区外键 OSH_ORDER_FKSOI_ORDER_FK 无法单独禁用,DISABLE 返回 ORA-14650。处理方式:保持启用,反向 EXEC 设为单工作线程,按父表到子表的顺序回放并校验。

EXEC 首次启动缺 GV_$LOCKGV_$SESSION 权限,报 DRS-9011/ORA-00942,补充后消失:

GRANT SELECT ON SYS.GV_$LOCK TO DMDRS; GRANT SELECT ON SYS.GV_$SESSION TO DMDRS;

Linux EXEC 启动需指定 DRS 与 Oracle Instant Client 动态库路径:

cd /drs/work/dm_to_oracle/target export LD_LIBRARY_PATH=/drs/dmdrs5/release:\ /home/dmdba/instantclient19_31/usr/lib/oracle/19.31/client64/lib tail -n 0 -f exec_cmd.input | \ /drs/dmdrs5/release/drsvr \ /drs/work/dm_to_oracle/target/exec_oracle.xml

DM CPT 首次失败:DRS-5125 LOAD DICT

DRS-5125 LOAD DICT
auxiliary table triggers is missing, number: 0, total: 4

源 DM 未安装 4 个 DRS DDL 辅助触发器。本轮不复制 DDL,选择系统表逻辑日志模式,设置 RLOG_APPEND_SYSTAB_LOGIC=1 后重启 DM,清理未使用的 CPT 运行目录,重启后出现:

DDL mode: SYS TABLES
CPT ready: dm8 cpt

判断 CPT 是否可用应以 ready/running 状态为准,不能只看管理进程存在。

网络连通。 CPT 启动后 Windows 连接长期处于:

192.168.79.1 -> 192.168.79.143:5346  SYN_SENT

Linux EXEC 已监听 0.0.0.0:5346,但防火墙只放行 5901/5236/5237。补充:

sudo firewall-cmd --permanent --add-port=5346/tcp sudo firewall-cmd --reload

此后连接变为 Established,EXEC 日志出现"来自源DMDRS的连接已建立"。

装载字典(指定 LSN 掩码)。 切换前普通表、5 张影子表和两端 UDT 语义已经一致,因此反向链路不再做全量。对 28 张不含 UDT 的普通业务表及 5 张普通影子表逐表装载字典并指定当前 LSN 掩码,只回传该 LSN 之后的 DM 事务:

alter cpt_dm8_drsuser add dict \
  "sch.name='MIG_APP' and tab.name='SALES_ORDER'" LSN

alter cpt_dm8_drsuser add dict \
  "sch.name='MIG_SYNC' and tab.name='CUSTOMER_FLAT'" LSN

现有演练验证了 28 张普通业务表可以生成字典;修订方案还需为 MIG_SYNC 下 5 张影子表生成字典,因此反向可捕获范围应为 33 张普通表。新增的 5 张表尚未实际验证,必须以实际字典清单和日志为准。

UDT 原表不进入反向 CPT。 4 张含 DM 自定义类型的表在现有演练的装载字典阶段直接返回 DRS-5539 不支持的自定义类型

DM 自定义类型列
CUSTOMER PHONES PHONE_VARRAY_T
CUSTOMER_ADDRESS ADDRESS_VALUE ADDRESS_OT
SHIPMENT DELIVERY_ADDR ADDRESS_OT
WAREHOUSE WAREHOUSE_ADDR ADDRESS_OT

反向验证还发现 WAREHOUSE 也会失败:DM 作为日志源装载字典时检查表的完整类型定义。列黑名单解决不了,因为失败发生在装载字典阶段,早于普通列映射。

(**问题根因:**DRS本身不支持DM—>Oracle 的自定义类型,但是支持Oracle到DM的自定义类型,因此前面Oracle—>DM同步时,加载字典没报错,到这里才报错)

修订方案不捕获这 4 张 UDT 原表,改由 DM 当前主库的生产组件在同一业务事务中维护 5 张影子表。反向链路如下:

image.png

Oracle 接收组件必须使用幂等逻辑:普通对象行按主键 MERGE/DELETE;电话号码数组按 CUSTOMER_IDPHONE_SEQ 重建。DRS 重放同一事务时不能产生重复电话、顺序变化或对象属性残留。接收组件不得自行提交,必须随 DRS 当前目标事务一起提交或回滚。

5.3 DM 业务模拟与校验

脚本主要逻辑(每轮一个事务,覆盖订单、明细、库存、支付、事件六类写入):

// DmCutoverWorkload.java:一轮事务 long orderId = nextOrderId(connection); // SEQ_ORDER_ID.NEXTVAL insertOrder(connection, orderId, channel, goods, tax, i); insertItem(connection, orderId, 1, 10003, "SKU-00003", ..., qty1, PRICE_10003); insertItem(connection, orderId, 2, 10004, "SKU-00004", ..., qty2, PRICE_10004); reserveInventory(connection, orderId, 10003, qty1, i); // 更新 INVENTORY,写 INVENTORY_TXN reserveInventory(connection, orderId, 10004, qty2, i); updateOrderStatus(connection, orderId, status, goods, tax); if (!"CANCELLED".equals(status)) { insertPayment(connection, orderId, goods.add(tax)); } insertEvent(connection, orderId, status, i); // 非空 JSON CLOB 事件 connection.commit();

现有演练先提交 1 笔验证事务,Oracle 收到完整关联数据(2 明细、1 历史、1 支付、2 库存流水、1 事件、空 LOB 为 0);随后再提交 19 笔,共 20 笔,订单范围 1010000~1010019。每笔事务覆盖订单插入与状态更新、两条明细、两次库存更新、两条库存流水、状态历史与审计、支付或取消分支、非空 JSON CLOB 事件。

修订后的影子表方案还需要补一组 UDT 专项事务,至少覆盖:

  1. 在 DM 新增客户并写入多个有序电话号码;
  2. 只修改 CUSTOMER.PHONES,不修改其他普通列;
  3. 同一事务同时修改客户普通列和电话号码;
  4. 修改 CUSTOMER_ADDRESS.ADDRESS_VALUE
  5. 修改 SHIPMENT.DELIVERY_ADDRWAREHOUSE.WAREHOUSE_ADDR
  6. 删除对象行、清空电话号码数组以及显式回滚事务;
  7. 等待反向 DRS 追平后,在 Oracle 同时比较业务表 UDT 属性和 5 张影子表标量。

停止 DM 写入并等待追平后校验:

切换前 DM 写入后 Oracle 回传后 增量
SALES_ORDER 22,732 22,752 22,752 +20
SALES_ORDER_ITEM 65,463 65,503 65,503 +40
ORDER_STATUS_HISTORY 24,918 24,938 24,938 +20
PAYMENT 16,185 16,202 16,202 +17
BUSINESS_EVENT 207,650 207,670 207,670 +20
CHANGE_AUDIT 9,296 9,356 9,356 +60
INVENTORY_TXN 4,376 4,416 4,416 +40

新订单范围的两端聚合结果一致:

orders=20  goods_sum=9196.38  tax_sum=1195.5294
paid=17  cancelled=3  items=40  item_qty=71
payments=17  inventory_txns=40  inventory_qty=-71
events=20  event_null_lobs=0  event_lob_len=970

5.4 回退 Oracle

停止 DM 写入,等待反向链路追平,并完成普通业务表、5 张影子表和 4 张 UDT 业务表的语义校验。全部通过后停止反向链路,顺序仍为先 CPT、后 EXEC:

stop cpt_dm8_drsuser
stop exec_oracle_drsuser

反向链路完全停止后切换影子组件角色:Oracle 禁用“影子表→业务表”、启用“业务表→影子表”,恢复为主库角色;DM 禁用“业务表→影子表”,如需重新建立正向同步链路,再启用“影子表→业务表”。必须先切角色再开放 Oracle 写入,否则回切后的第一笔 UDT 业务不会进入 Oracle 影子表。

恢复 Oracle 写入前,序列按 DM DBA_SEQUENCES.LAST_NUMBER 的缓存上界推进:

ALTER SEQUENCE MIG_APP.SEQ_ORDER_ID RESTART START WITH 1011000;

其余序列目标值:SEQ_EVENT_ID 20060000SEQ_HISTORY_ID 2015000SEQ_INV_TXN_ID 1020000SEQ_PAYMENT_ID 1513000SEQ_SHIPMENT_ID 1604000SEQ_AUDIT_ID 24001SEQ_VALIDATION_ID 501

随后恢复 4 个 LOB 列 NOT NULL、18 个外键 ENABLE VALIDATE(全部成功)、5 个业务触发器;DDL 审计触发器保持禁用。最终状态:

ENABLED_BUSINESS_TRIGGERS = 5
DDL_AUDIT_STILL_DISABLED  = 1
DISABLED_RECEIVE_FKS      = 0
NULLABLE_LOB_COLUMNS      = 0

最后验证时,先调用原 MIG_APP.PKG_ORDER_API 完成下单、提交与支付,再执行一笔 UDT 修改,确认 Oracle 主库的“业务表→影子表”生产组件已经恢复。普通订单脚本主要逻辑(oracle_post_cutback_smoke.sql):

MIG_APP.PKG_ORDER_API.CREATE_ORDER( p_tenant_id => 1, p_customer_id => 1003, p_warehouse_id => 11, p_channel_code => 'WEB', p_items => MIG_APP.ORDER_LINE_NT( MIG_APP.ORDER_LINE_OT(10003, 1, null), MIG_APP.ORDER_LINE_OT(10004, 2, null) ), p_order_date => trunc(sysdate), p_order_time => systimestamp, p_order_id => l_order_id ); MIG_APP.PKG_ORDER_API.SUBMIT_ORDER(l_order_id); MIG_APP.PKG_ORDER_API.CAPTURE_PAYMENT( p_order_id => l_order_id, p_payment_ref => 'ORACLE-CUTBACK-' || l_order_id, p_payment_method => 'WECHAT', p_paid_at => systimestamp ); commit;

执行结果:

ORACLE_CUTBACK_WRITE_OK order_id=1011000
order_status=PAID  item_count=2  hist_count=2
pay_count=1  inv_txn_count=2  event_count=3

6. 问题汇总

问题 现象 处理
全量内存超限 目标端内存约 1 GB,DRS 使用约 2 GB 后停止 表级串行,限制装载线程与缓存
VPD 过滤 迁移账户读取结果为零行或不完整 GRANT EXEMPT ACCESS POLICY,受控使用
LOB 两阶段写入 临时空值被 NOT NULL 拒绝 4 列接收期放宽,校验后恢复
UDT 绑定失败 正向 VARRAY/OBJECT 绑定失败;反向 UDT 原表无法装载字典 4 张 UDT 原表不直接进入 DRS;改为 5 张语义影子表,正反向均同步普通 DML;
CVT 适用范围 CVT 支持普通 DML 清洗,但配套手册排除数据库自定义类型 CVT 只清洗普通字段,不直接处理 VARRAY/OBJECT
空 LOB 行数与最大键相等,588 行 LOB 为空 LOB 定向修复后恢复约束
触发器不兼容 DM 无法解析 STANDARD_HASH 改写为 DBMS_CRYPTO.HASH_SH256
反向字典失败 DRS-5125,DDL 辅助触发器缺失 RLOG_APPEND_SYSTAB_LOGIC=1,系统表逻辑日志
引用分区外键 DISABLEORA-14650 保持启用,单工作线程(避免子表抢先导致链路报错),父→子顺序校验
反向连接不通 Windows 连接停在 SYN_SENT 防火墙放行 5346
EXEC 权限不足 DRS-9011/ORA-00942 授予 GV_$LOCKGV_$SESSION
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服