2026-10-31  ·  日常运维

Flink CDC 接达梦:卡住你的不是连接器,是日志里的 ID

日常运维

本文所有命令和输出都在 DM8(8.1.3.140,Rocky Linux 8.6,单机)上实跑取得,原文照抄,没有改写。Flink 侧的连接器行为本文未实测,涉及处一律标注,不给数字。

参数开了,你看到的还是看不懂

按网上教程配完 CDC,参数也改了、实例也重启了,兴冲冲去查V$LOGMNR_CONTENTS,屏幕上出来的是这个:

INSERT INTO "150994945"."OBJ# 1035"("COL 1", "COL 2", "COL 3", "COL 4") VALUES (...)

三个问号就冒出来了:150994945 是谁,OBJ# 1035 是哪张表,COL 1 又是哪一列。

这不是你配错了,也不是驱动版本低。达梦的日志挖掘结果本来就是这样:它给的是对象编号,不是名字。要变成能读的东西,得自己查字典表翻译。

这篇文章把达梦这一侧的三个关说清楚。三关都过了,数据才轮到连接器去读。

第一关:参数不是「必须等于 1」,是「必须非 0」

网上流传最广的一句是「必须 RLOG_APPEND_LOGIC=1,否则 V$LOGMNR_CONTENTS 永远为空」。

前半句对(0 确实挖不到),后半句的「=1」不准确。达梦官方手册的原话是置为 1、2、3 或 4,取值不同,能挖到的信息量不同。

实测:0 和 1 到底差在哪

同一台库、同一批 DML(3 条 INSERT、1 条 UPDATE、1 条 DELETE,外加建表删表),只在两个参数值下各跑一遍,然后切归档、挖新生成的那个归档文件。

参数为 0 时,整个归档文件的结果是:

select count(*) from v$logmnr_contents;        -- 0
select operation, count(*) from v$logmnr_contents group by operation;
-- (空结果集,一行都没有)

注意:不是「挖不到这张表的 DML」,是整个文件一行都挖不出来,连 START/COMMIT 都没有。所以「0 才空」这句是准确的。

把参数改成 1、重启实例、重新造同样的数据、再挖,同一个归档文件的结果:

select count(*) from v$logmnr_contents;        -- 21

OPERATION   COUNT(*)
---------   --------
START              7
COMMIT             7
DDL                2
INSERT             3
UPDATE             1
DELETE             1

DML 一条不少。差异一目了然。

怎么改,以及一个容易漏的点

-- 第二参数 2 = 同时改内存和 dm.ini;给 1 只改内存,重启就白改了
call sp_set_para_value(2, 'RLOG_APPEND_LOGIC', 1);
# 这是静态参数,必须重启实例才生效
systemctl restart DmServiceDMSERVER

改完去 dm.ini 里确认那一行真的变了,再看 V$DM_INI 里的值。两处都对,才说明重启后生效了。

第二关:DML 记录的 TABLE_NAME 是空的

这一关最阴,它让你以为自己挖到了,其实全被过滤掉了。

我第一遍查用的就是最自然的那句:

select count(*) from v$logmnr_contents where table_name = 'LOGCASE_T';

返回 2,看着像有数据。但紧接着按操作类型分组,发现这 2 条都是 DDL——3 条 INSERT、1 条 UPDATE、1 条 DELETE 全不见了。

原因:只有 DDL 记录的 TABLE_NAME 有值,DML 记录的 TABLE_NAME 是 NULL。

改成不带表名过滤,看全貌:

select operation, table_name, seg_owner, row_id from v$logmnr_contents where rownum <= 6;
OPERATION  TABLE_NAME  SEG_OWNER     ROW_ID
---------  ----------  ------------  ------------------
START      <null>      <null>        <null>
DDL        LOGCASE_T   SYSDBA        <null>
COMMIT     <null>      <null>        <null>
INSERT     <null>      150994945     AAAAAAAAAAAAAAAAAB
...

能定位到 DML 的只剩三个字段:SEG_OWNER(是对象 ID,不是用户名)、ROW_ID、DATA_OBJ#。

所以按表名过滤 DML 这件事,在达梦上必须先自己翻译出表名。照抄 Oracle 那句 where table_name = '...',得到的结果会安静地少掉九成数据——不报错,只是不对。

第三关:ID 要自己查字典翻译

回到那条大家都看不懂的 INSERT。把它涉及的三个 ID 拿去查 SYSOBJECTS:

select 'OBJ' as src, id, name, type$ from sysobjects where id = 1035;
select 'SEG' as src, id, name, type$ from sysobjects where id = 150994945;
select 'USR' as src, id, name, type$ from sysobjects where id = 50331649;
SRC  ID          NAME        TYP
---  ----------  ----------  ------
OBJ  1035        LOGCASE_T   SCHOBJ
SEG  150994945   SYSDBA      SCH
USR  50331649   SYSDBA      UR

对上了。SQL_REDO 里的结构是这样的对应关系:

日志里写的实际是什么怎么翻译
"150994945"模式(SCHEMA)IDsysobjects.id → 模式名
"OBJ# 1035"表 / 对象 IDsysobjects.id → 对象名
"COL 1"…"COL 4"列序号(不是列名)syscolumns 按列序反查
USERNAME = 50331649用户 IDsysobjects(type=UR)

所以达梦的日志挖掘给你的是「谁、对哪个对象、做了哪个操作、旧值新值是什么」,但「对象叫什么、哪一列叫什么」这一层,留给调用方自己解。

这也是为什么很多自己写的 CDC 脚本能跑却对不上数据——它按列序去映射,一旦表加过列、改过列顺序,就对歪了。

顺带一个必踩的坑:LOGMNR 是会话级的

这些操作必须在同一个数据库连接里完成:

call dbms_logmnr.add_logfile('/dmdata/dmarch/DAMENG/ARCHIVE_LOCAL1_..._2026-10-31_10-03-50.redo', 1);
call dbms_logmnr.start_logmnr();
select count(*) from v$logmnr_contents;     -- 必须在 start 之后、同一个连接里
call dbms_logmnr.end_logmnr();

换一个连接去查,报的是这句:

dm.jdbc.driver.DMException: dbms_logmnr.start_logmnr() must be invoked before selecting from v$logmnr_contents

所以「用 A 工具 start、用 B 工具查」这条路走不通。用 disql 一次跑完没问题(同一个会话), 用驱动写脚本就很容易在这里翻车。

切归档这一步,别漏

只改了参数、只跑了 DML,归档文件还没切换的话,那批操作可能还在当前日志里没落盘成归档。 DBMS_LOGMNR 只认归档日志,所以要显式切一下:

alter system switch logfile;

切完去 V$ARCHIVED_LOG 按 SEQUENCE# 倒序看最新那个文件,路径直接用它的。别自己拼文件名。

顺带一句:alter system 这类语句在 JDBC 下会报 Operation does not support using PREPARE way——不是权限问题,是它不接受预处理方式。 写脚本时遇到这句,换成直接 Statement.execute() 就好。

三关都过了,才轮到连接器

把上面的顺序倒过来看,就明白为什么「CDC 连不上 / 读不到数据」这类问题里, 很大一部分其实跟连接器没关系:

顺序要做的事没做的现象
1开归档 + RLOG_APPEND_LOGIC 非 0挖出来永远是空
2确认能挖到 DML(别按表名过滤)以为没数据,其实是过滤掉了
3把对象 ID 翻译成表名列名数据能同步,但字段对歪

关于 Flink 这一侧,本文没有实测。我没在本文环境里搭过 Flink 集群,所以连接器版本兼容性、断点续传、全量转增量的切点这些,我不给数字也不给结论。这一半在另一台机器上做,跑完单独成篇。

能确定的只有边界:凡是从达梦归档日志里读变更的实现,都要过上面三关。连接器只是帮你把第 3 步做了,它改变不了第 1、2 步。

回头说三关的份量

官方文档说「置为 1、2、3 或 4」,第三方文档说「必须等于 1」,而实际跑起来你会发现真正的工作量在第 3 步:把 ID 翻译成名字。前两关十分钟,第三关决定这套方案能不能交付。

如果你也在达梦上做 CDC,卡在哪一步、报错原文是什么,欢迎在 工具仓库提个 issue。 我这边有一套可复跑的探针脚本,报错对上号我可以直接告诉你卡在第几关。

几个常被问到的问题

参数到底该选 1 还是 2?

本文只实测了 0 和 1,2/3/4 未实测,不给建议。能确定的是:0 必然空; 1 下 UPDATE/DELETE 会记录非主键列,且 UNDO_VALUE 有值(我这条 INSERT 是 UNDO_VALUE=15)。 按官方手册的说法,3 只记 ROWID、挖不到 UNDO 值。具体取舍建议在你自己环境里按同样的方法跑一遍。

开了这个参数,redo 会不会暴涨?

未实测,不给数字。能确定的是:只要不是 0,写日志时就会额外记逻辑记录,redo 增长是必然的。 增长多少取决于你的更新频率和列宽,这个必须在自己的库上量。归档空间要提前留够—— dmarch.ini 里的 ARCH_SPACE_LIMIT 就是干这个的。

为什么我按 table_name 过滤,DML 一条都没有?

因为 DML 记录的 TABLE_NAME 是 NULL(见第二关)。要么先翻译出对象 ID 再按 SEG_OWNER 过滤,要么去掉表名条件看全量。

V$LOGMNR_CONTENTS 有 63 列,常用的就那几个?

SCN、TIMESTAMP、XID、OPERATION、OPERATION_CODE、 SEG_OWNER、ROW_ID、DATA_OBJ#、USERNAME、 SQL_REDO、SQL_UNDO、UNDO_VALUE、REDO_VALUE。 另外 XID 是二进制,直接打印会把终端当成二进制文件处理,记得转成十六进制再看。

能不能绕过 LOGMNR 直接读 redo 文件?

未实测。达梦没有公开的 redo 解析接口,官方路径就是 DBMS_LOGMNR。 自己解析 redo 属于非公开格式,版本一变就可能失效,不建议作为交付方案。

这个结论在集群上成立吗?

本文是单机(DM8 8.1.3.140,Rocky Linux 8.6)。DSC / 数据守护环境下每个节点的归档、 SEG_OWNER 的编号规则是否一致,未实测。上集群前建议先按本文方法在单节点验证一遍。

本文测于 2026-10-31。达梦版本 8.1.3.140(1-3-140-2026.08.03-340005-20093-ENT), Rocky Linux 8.6,单机实例,归档本地磁盘。

← 返回目录