本文所有命令和输出都在 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)ID | sysobjects.id → 模式名 |
"OBJ# 1035" | 表 / 对象 ID | sysobjects.id → 对象名 |
"COL 1"…"COL 4" | 列序号(不是列名) | syscolumns 按列序反查 |
USERNAME = 50331649 | 用户 ID | sysobjects(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,单机实例,归档本地磁盘。