LogMiner vs 裸日志解析(十五):给表中间加一列,为什么能把同步链路搞挂?
系列第 15 篇。前一篇聊了"接入前要先改生产库的配置",这一篇聊一件更日常的事:schema 一变,链路就可能出问题。
一句开发最常说的话
"我就加个字段,很快的。"
做数据同步的人,最怕听到这句话。因为对不少 CDC 方案来说,表结构一动,链路就可能悄悄坏掉——不是报错停摆那种坏,是"还跑着,但数据已经不对了"。
01 真实场景:加了一列,下游整张表错位
朋友公司一个订单同步任务,跑了大半年没出过事。
某天开发要给ORDERS表加个CHANNEL字段,顺手加在了表中间(他想让新字段挨着STATUS,看着顺眼)。
任务没报错,也没停。第二天对账,数据却对不上——下游ORDERS表里,AMOUNT里装着原来的STATUS,STATUS里装着原来的CREATE_TIME……从新列往后,整排错位。
他查了一整天,最后发现根因就俩字:加列。
02 底层原理:LogMiner 靠的是"当下的数据字典"
要理解为什么会错位,得先知道 LogMiner 是怎么把日志翻译成人话的。
redo 日志里记的不是"表名、列名",而是对象号 + 列号(column position)——物理层面的编号。LogMiner 要输出你能看懂的INSERT INTO ORDERS (ORDER_ID, ...) VALUES (...),就得拿一份"号码 → 名字"的对照表去翻译。
问题就在这份对照表:它来自数据库当前的数据字典,不是日志产生那一刻的快照。
列一旦变动,编号和名字的对应关系就变了:
- 中间插列→ 插入点之后的列号全部后移,旧日志按新字典翻译 →整排错位(最典型);
- 改列类型→ 解析出来的值和类型对不上;
- 删列 / 重排列→ 同样对不上;
- DROP TABLE / TRUNCATE→ 下游要么收到一堆无处安放的变更,要么该清空的没清空。
LogMiner 的 DDL 跟踪本身也不完整:它没法把每一种 DDL 都准确翻译成下游能消费的事件,新建的物化视图也不会自动回填。
03 社区里的同款
这不是孤例。翻一翻 Debezium 和 Flink CDC 的文档与 issue,关于 schema 变化的限制写得都很克制,但很实在:
- Debezium 对 Oracle 给了一份明确的DDL 支持列表,没在列表里的 DDL,行为是不保证的;
- Flink CDC 的 Oracle 连接器长期被反馈DDL 同步不完整、Schema Evolution 能力有限;
- 常见的下游症状:Kafka Connect 报反序列化失败、目标表结构对不上、任务反复重启。
一句话:在 Oracle 上做 schema 同步,是件很脆的事。
04 麻烦在哪
- "加个字段"是最日常的开发动作,却可能让链路静默损坏——可怕的不是崩溃,是"看起来没事"。
- 错位往往是事后发现的:等对账、报表口径不对了才回头查,已经积压了一堆脏数据。
- 修起来更麻烦:要停任务、清目标、回溯重放,还得跟业务对账,一次能折腾好几天。
- 历史日志救不了:就算你有半年前的归档日志,拿"现在的字典"去挖,一样错位。
05 裸日志解析:自己带一份"会演进的字典"
换个思路:既然错位的根子是"字典只有当下这一份",那就把字典也当成需要持续演进的对象来管。
- DML + DDL 一起采:不只解析行变更,DDL 也当事件捕获,沿时间线维护一份会演进的结构信息;
- 按"日志产生时"的结构还原:解析某一段日志,用的是那一段对应时刻的结构,而不是今天的;
- schema 变更平滑过渡:加列、改类型、删列这些动作,链路自己消化,不把问题甩给下游;
- 粒度可控:表、列的过滤与映射,由自己掌握。
这就是"直接读二进制"能干、而"套数据库接口"干不了的事——结构信息从日志里来,字典自己维护,不依赖数据库当下的状态。
06 反直觉的一点
直觉上,"字段变多了"是信息更丰富了,同步应该更容易才对。
但在 Oracle 日志解析这条路上,它反而是一个高风险动作。原因很别扭:日志记的是编号,不是名字;编号对应的名字,取决于你"问谁、什么时候问"。翻译字典的时效性,比字段本身更重要。
07 说句实在话
schema 演进做得好不好,平时看不出来——直到业务真加了个字段。
这是很多团队上线时不会重点测、上线后一定会踩的一环。我们的选择是:把结构演进的账自己算清楚,让"加个字段"回到它本来的样子——一件小事。
往期回顾
- (十一)只想采一个 PDB,却要牵连整个 CDB
- (十二)存储堆到顶配,为什么还是慢
- (十三)想从备库采数,为什么要先动容灾
- (十四)为了采数据,为什么要先动生产库的配置
- (十五)给表中间加一列,为什么能把同步链路搞挂 ← 本篇
声明:文中 DDL 支持范围引自 Debezium 官方文档的 Oracle DDL 支持说明与 Flink CDC 社区讨论;LogMiner 使用"当前数据字典"翻译日志对象的机制,可参考 Oracle 官方 Database Utilities 手册。实际表现受数据库版本、DDL 类型与下游实现影响,请以自身环境实测为准。