
简介FlinkCDC与达梦数据库基于日志的实时同步方案是一套面向大数据工程师、数据仓库运维及实时计算开发者的实践资源包。该方案通过解析达梦数据库日志捕获增、删、改等变更操作相比传统定时抽取能显著降低对源库的性能影响并满足低延迟、高可靠的数据同步需求适用于实时数仓、数据监控、告警报表等场景。资源共5个文件涵盖FlinkCDC连接器jar包、JDBC驱动、可运行的参考工程、sql初始化脚本以及docx格式的用户手册方便不同技术栈的开发者对照使用整体包体约35.48MB。目前已有2085人学习浏览可见该方向关注度较高。读者可通过用户手册快速掌握连接器配置与驱动依赖关系再结合参考程序与初始化脚本即可自主搭建FlinkCDC连接达梦数据库的同步任务并进一步扩展出符合自身业务的事件驱动应用。1. FlinkCDC 同步达梦数据库基于日志解析的实时同步怎么做FlinkCDC 配合达梦数据库做基于日志解析的实时同步是我在数据平台项目里反复验证过的路线开启归档日志由 CDC 连接器直接读取重做日志里的变更记录再经由 Flink 分发到下游。相比轮询或触发器方案它不侵入业务表不依赖业务时间字段删除和更新都能完整捕获。这篇文章把选型依据、连接器配置、启动参数和实战踩坑记录拆开讲适合正在做达梦到数仓或异构库同步的读者也适合第一次接触达梦日志同步、不知道从哪儿下手的人照着搭。2. 选型与原理为什么基于日志而不是轮询2.1 基于日志解析达梦 CDC 的底层机制达梦数据库是类 Oracle 体系事务提交后变更会写进联机重做日志归档模式开启后又会把历史日志落盘。Flink CDC 做的事情本质上是给达梦套了一层 Debezium 引擎连接器伪装成数据库客户端读取日志中的变更向量解析成 INSERT、UPDATE、DELETE 三类事件再统一成 Flink 的 DataStream。整个过程不碰业务表结构不要求表里有 modify_time 这类字段。先确认你的达梦实例是否处于归档模式这一步直接决定后续能不能读到日志-- 检查数据库是否处于归档模式 SELECT ARCH_MODE, STATUS FROM V$DATABASE; -- ARCH_MODE 为 Y 表示已开启归档N 表示未开启这条语句在 DM8 上直接能跑通SYSDBA 登录后执行即可。如果返回的不是 Y说明实例还停留在非归档状态CDC 连接器只能做一次全量快照无法持续拿到增量日志。归档开关属于实例级配置修改后通常需要重启数据库实例才能稳定生效所以建议在维护窗口做不要赶在业务高峰期临时开。只开归档还不够达梦的日志在默认情况下可能只记录变更行的主键和少量信息UPDATE 场景下游拿不到完整的新值镜像。这里面有个跟 Oracle 补充日志类似的机制-- 开启数据库级补充日志 ALTER DATABASE ADD SUPPLEMENTAL LOG; -- 确认补充日志状态 SELECT * FROM V$SUPPLEMENTAL_LOG;V$SUPPLEMENTAL_LOG 返回的记录里能看到对应字段位点如果不确定查询结果含义可以先用一条带主键的 UPDATE 做实验修改一个非主键列然后看 CDC 输出里是否携带了完整行数据。常见做法是先开库级补充日志权限不足时再退到表级逐表补充。连接器拿到日志后的处理链路也值得说清楚。Debezium 引擎会先记录当前日志位点再把存量数据通过 JDBC 快照读出来之后从刚才记录的位点继续消费增量。位点的保存方式对不同版本有差异老版本依靠 Checkpoint 里的 state 保存新版本则拆分了快照进度和日志消费进度。两个阶段衔接得好不好直接影响你到底是全量增量无缝还是中间缺一段。2.2 为什么轮询和触发器方案都要退让达梦数据同步在社区里常见三种做法业务表加时间字段轮询、触发器写中间表、基于日志解析。前两种看着简单落地后全是细节问题这里直接放一张对比表方案最小延迟删除捕获业务侵入维护成本适合场景时间字段轮询秒到分钟级不支持需要改表加字段低容忍删除丢失、表结构可控触发器写中间表毫秒级支持极高DML 路径加开销高小表、DBA 能长期介入基于日志解析百毫秒级支持无中生产核心表、异构同步时间字段轮询最大的坑在于 UPDATE 不改时间字段时会漏数很多业务系统里 update_time 的维护完全依赖开发规范规范一松就出问题。触发器方案虽然能捕获所有 DML但会给生产库写入路径增加不可忽略的开销同时每次表结构变更都要同步改触发器逻辑一个表还好上百张表就是灾难。基于日志解析几乎没有业务侵入。数据库自己就在写日志连接器只是多了一个读者。删除操作也能捕获因为日志里记录了 delete 向量的主键信息。代价是需要对达梦的日志机制有一定了解出问题时排查链路比轮询长所以我把常见问题集中放在后面专门讲。2.3 版本与组件选型驱动、连接器、Flink 之间怎么配选型这块我踩过不少坑核心结论是先确认达梦实例的版本再倒推驱动和连接器版本。达梦 DM8 是当前最常见的主力版本JDBC 驱动一般对应 dm-jdbc-18 这一系列类名在不同驱动版本里有差异老版本是 dm.jdbc.driver.DmDriver新版本变成了 DmJdbcDriver18。连接器会按自己的实现去加载驱动如果你图省事只在作业里配了个驱动名结果类名对不上任务一启动就 ClassNotFound。组件搭配方面我一般会按下面这个思路做约束组件建议理由Flink 版本优先 1.16 及以上新版 Checkpoint 机制和 CDC 框架兼容性更好CDC 连接器社区适配达梦的 dm-cdc 系列官方连接器不直接支持达梦需要适配层JDBC 驱动与 DM8 匹配的 dm-jdbc-18版本不一致会导致日志解析或事务查询异常下游MySQL / Kafka / 另一个达梦库JDBC 或 Kafka Sink 均可取决于架构有一点要特别提醒网上能搜到很多达梦改 Oracle 连接器就能跑的说法实际很难走通。达梦的日志解析接口、系统视图、补充日志机制跟 Oracle 同名但细节不同强行复用 Oracle 连接器容易在启动时通过校验、跑一段时间后突然丢日志。宁可多花半天确认连接器对达梦的支持边界也不要迷信这种捷径。3. 落地步骤连接器配置、任务启动与参数调整3.1 环境准备归档、补充日志和账号权限在写任何 Flink 作业之前先把达梦这边的环境准备好。需要三样东西SYSDBA 权限账号、归档日志、补充日志。账号用途不只是连接查询还要能读取日志视图权限不足会在启动阶段报 ORA 风格的无权限错误。# 使用 disql 登录达梦实例示例disql SYSDBA/密码localhost:5236 disql SYSDBA/passwordlocalhost:5236 # 查看当前数据库模式和实例状态 SELECT NAME, STATUS$ FROM V$INSTANCE; # 查看归档配置 SELECT * FROM V$DM_ARCH_INI;V$DM_ARCH_INI 是达梦归档配置视图返回的 DEST 字段能看出归档目录落在哪个磁盘。这里有个实际经验归档目录容量设计要留足很多生产环境的同步中断都是因为归档日志写满磁盘导致数据库直接 hang 住。建议归档和数据库文件分开存储归档文件的大小和空间限制按业务峰值估算。账号准备上还有一个细节Flink 作业运行账号和日常业务账号要分开。CDC 连接器需要查询日志视图、获取表结构、读取快照给它一个最小权限账号更安全同时也方便出问题时从数据库侧审计连接来源。别直接拿业务应用的高权限账号来跑同步任务日志同步链路一旦配置错误影响面会非常大。3.2 Flink SQL 方式一条 CREATE TABLE 拉起同步环境就绪后最快的验证方式是 Flink SQL。Flink SQL 把连接器封装成动态表DDL 里声明的字段和主键会映射到 CDC 输出的事件结构上。CREATE TABLE dm_user_src ( id BIGINT PRIMARY KEY, name STRING, dept_id BIGINT, updated_at TIMESTAMP(3), PRIMARY KEY (id) NOT ENFORCED ) WITH ( connector dm-cdc, hostname 10.0.0.11, port 5236, username SYSDBA, password Dm_Test_2024, database-name DAMENG, schema-name TEST, table-name T_USER, scan.startup.mode initial, debezium.log.mining.strategy redo_log_catalog, debezium.database.history.kafka.topic dm-history-topic, debezium.database.history.kafka.bootstrap.servers 10.0.0.21:9092 );先解释这段 DDL 里的关键参数。connector 指定为 dm-cdc这是社区适配达梦的连接器标识。database-name 填达梦实例名schema-name 在达梦里等于用户名TEST 就是登录用户对应的模式table-name 支持正则匹配多表。scan.startup.mode 有三个可选值initial 表示先做全量快照再接增量latest-offset 表示跳过存量只消费启动后的日志earliest-offset 表示从最早可用归档日志开始。我一般建议生产环境首次上线用 initial后续重复跑验证用 latest-offset。debezium.log.mining.strategy 是参考 Debezium 体系设计的日志挖掘策略。redo_log_catalog 优先从归档日志解析适合同步任务与业务错峰运行online_catalog 直接读联机日志延迟更低但要求连接器能实时跟上日志产生速度。具体支持程度取决于你拿到的连接器版本确认不了就两个都跑一遍看日志输出频率。再配下游表CREATE TABLE mysql_user_sink ( id BIGINT PRIMARY KEY, name STRING, dept_id BIGINT, updated_at TIMESTAMP(3), PRIMARY KEY (id) NOT ENFORCED ) WITH ( connector jdbc, url jdbc:mysql://10.0.0.22:3306/dw_ods, table-name ods_dm_t_user, driver com.mysql.cj.jdbc.Driver, username dw_sync, password Sync_2024 ); INSERT INTO mysql_user_sink SELECT id, name, dept_id, updated_at FROM dm_user_src;JDBC Sink 表声明了主键后写入会走 UPSERT 语义源表更新事件落到目标表时按主键做更新而不是追加。这里有个容易被忽略的点目标表要提前建好主键或唯一索引否则 UPSERT 退化成普通 INSERT重复事件会把数据打重。INSERT INTO SELECT 的作业提交到 Flink 集群后可以在 SQL Client 里直接观察任务状态数据量不大时不建议再包一层 DataStream直接用 SQL 效率更高。3.3 DataStream 方式适合需要自定义序列化的场景Flink SQL 能覆盖八成场景但遇到复杂清洗、多流关联、需要自定义 sink 协议时还是要落到 DataStream API。Java 代码的骨架大致是这样// 按实际 jar 包导入包路径以连接器版本为准 import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import com.xxx.dm.cdc.DmCdcSourceBuilder; import com.xxx.cdc.formats.JsonDebeziumDeserializationSchema; StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(); DmCdcSourceBuilderString source DmCdcSourceBuilder .builder() .hostname(10.0.0.11) .port(5236) .username(SYSDBA) .password(******) .database(DAMENG) .schemaList(TEST) .tableList(TEST.T_USER) .deserializer(new JsonDebeziumDeserializationSchema()) .build(); env.addSource(source) .map(json - { // 这里可以从 Debezium 的 Json 结构里提取 before/after 字段 // 按目标表结构组装成新的 RowData return json; }) .print(); env.execute(dm-cdc-sync-job);这段代码里builder 的 hostname、port、username、password 对应达梦连接参数schemaList 和 tableList 控制监听的范围。tableList 的格式是模式名.表名支持传多个。deserializer 决定输出格式JsonDebeziumDeserializationSchema 输出的是 Debezium 标准 JSON包含 before、after、op 等节点后续清洗逻辑需要自己解析 op 字段判断操作类型。DataStream 方式最直接的好处是可以在 map 阶段做类型转换和字段过滤。比如源表有 30 个字段但下游只需要 5 个在这里提前裁剪能降低下游写入压力。另外如果你要接的不是 JDBC sink 而是自研协议或者需要把事件同时发到 Kafka 和 RedisDataStream 的扩展性会舒服很多。3.4 Checkpoint 与恢复参数实时同步的后悔药不管用 SQL 还是 DataStreamCheckpoint 配置都不能省。CDC 任务本质上是一个持续运行的流作业日志消费位点保存在状态里只有 Checkpoint 成功位点才会固化。// 开启 Checkpoint间隔 30 秒 env.enableCheckpointing(30_000); // Checkpoint 相关配置 CheckpointConfig cpConfig env.getCheckpointConfig(); cpConfig.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE); cpConfig.setMinPauseBetweenCheckpoints(10_000); cpConfig.setCheckpointTimeout(60_000); cpConfig.setTolerableCheckpointFailureNumber(3); cpConfig.setExternalizedCheckpointCleanup( ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);这里逐项说下参数含义。enableCheckpointing(30_000) 是每 30 秒触发一次 Checkpoint间隔太短会增加数据库快照查询频率太长则故障恢复时重放日志的窗口变大。setMinPauseBetweenCheckpoints 控制两次 Checkpoint 之间的最小间隔防止前一个还没结束下一个就开始。setTolerableCheckpointFailureNumber 设为 3意思是连续失败 3 次才让作业失败避免下游短暂抖动就直接杀掉同步链路。最后一行是真正的后悔药RETAIN_ON_CANCELLATION 表示作业被手动取消时Checkpoint 数据不会被清理。这意味着你可以随时 cancel 任务做代码升级再从最近一次 Checkpoint 恢复继续消费日志位点从状态里读出来不会重复也不会丢。生产环境我强烈建议打开这个选项否则一次误操作就得重新全量同步。4. 踩坑与排查五条实战记录及解决过程4.1 现象、原因与解决方案坑 1任务启动即报 ClassNotFoundException定位到 dm.jdbc.driver.DmDriver。现象作业提交后 Source 算子反复重启日志里出现找不到驱动类的异常。原因达梦 JDBC 驱动没有打进 Flink 集群的 lib 目录或者驱动包被连接器的打包配置过滤掉了。不同版本的驱动类名还不一致老的用 dm.jdbc.driver.DmDriver新的用 DmJdbcDriver18按错误提示里的类名去比对最容易定位。解决把对应版本的 dm-jdbc jar 放到所有 TaskManager 的 lib 目录重启作业。如果用的是 Flink SQL Gateway 模式确认 SQL 会话的 jar 管理也包含了该驱动。以后每次升级驱动版本第一件事就是核对类名和版本号别只看文件名。坑 2UPDATE 同步到下游后只有主键字段变化非主键列全部是旧值。现象源库执行 UPDATE 修改了 name 和 dept_id下游表收到的更新事件里只有 id其他字段还是快照阶段的值。原因数据库层面补充日志没开全。达梦默认日志记录粒度在特定配置下不会携带完整行镜像Debezium 只能拿到主键和变更位点无法构造出完整的新值。解决先执行 ALTER DATABASE ADD SUPPLEMENTAL LOG 开库级补充日志再通过 V$SUPPLEMENTAL_LOG 确认生效。如果权限受限可以对同步的表单独设置表级补充日志。改完配置后一定要用一条真实 UPDATE 验证非主键字段能完整读到这一步不要省。坑 3TIMESTAMP 字段整体偏移 8 小时日期看起来错乱。现象源库表里 updated_at 是 2024-06-01 12:00:00同步到 MySQL 后变成 2024-06-01 04:00:00。原因数据库会话时区与 Flink 作业 JVM 时区不一致日志里的时间戳被连接器按系统默认时区解释常见于部署机是 UTC 而数据库是东八区的场景。解决把数据库连接会话时区、Flink 作业时区、下游连接时区统一设置Java 进程启动参数加上 -Duser.timezoneAsia/Shanghai。这种问题不会报错只能靠数据对比发现所以我在验证阶段会专门挑一个时间字段做抽样对比。坑 4源库执行 ALTER TABLE 后作业直接失败或状态卡死。现象业务侧给表加了一个字段几分钟后同步作业报 schema 不匹配或者一直停留在重试状态。原因连接器对 DDL 变更的支持有限schema 历史存储里没有同步到新的表结构。达梦不具备 MySQL binlog 那样成熟的 DDL 解析链路很多适配连接器默认只处理 DML。解决上线前先确认连接器是否支持 DDL 解析不支持就建立变更管理流程先暂停同步执行 DDL再更新下游表最后重启任务。也可以考虑把 DDL 变更频率高的表单独拆分用 DataStream 方式做字段容错在解析层忽略未知字段。坑 5任务运行正常但一直没有增量数据端到端延迟飙升。现象Flink UI 显示任务正常Checkpoint 也成功但下游表数据不变化source 的输入行数指标一直不涨。原因日志挖掘起点早于当前保留的归档日志Debezium 引擎尝试从已被清理的日志位点开始解析导致卡在等待状态。常见于归档日志保留窗口太短连接器启停频繁的场景。解决调大归档日志保留空间或数量把归档目录独立到容量充足的磁盘。如果暂时不需要回补旧数据可以把 scan.startup.mode 改成 latest-offset 跳过历史日志先恢复实时链路再找机会做全量校验。4.2 从排查思路看日志同步的黑匣子怎么打开第一次接触日志同步的人最头疼的是链路里每个环节看起来都在运行却不知道问题出在哪。我一般会按固定顺序排查先看 Flink UI 上 Source 算子的输入记录数和 Checkpoint 状态确认数据到底有没有流入再看 TaskManager 日志里有没有 Debezium 的关键词比如 snapshot、mining、offset最后到数据库侧查当前归档日志位点确认连接器消费的位置没有过期。日志同步的黑匣子主要集中在日志解析进度和下游写入进度之间的差距上。如果数据在源端已经更新但 delay 指标持续走高优先怀疑日志挖掘线程被大事务阻塞。达梦在处理大事务时日志生成量大Debezium 引擎要按事务维度组包一个长时间不提交的事务会把后续日志都挡在后面。遇到这种情况可以用一条小事务的 UPDATE 做探针观察它是否能在预期时间内到达下游以此判断是链路阻塞还是数据本身没产生日志。5. 进阶验证断点续传、存量增量衔接与性能观察5.1 断点续传与存量增量衔接验证同步任务上线前我习惯做一次完整的断点续传演练。步骤是先让任务跑几分钟确认日志消费位点推进然后手动 cancel 作业在源库执行一条 UPDATE最后从最近的 Checkpoint 或 Savepoint 恢复任务验证这条 UPDATE 能准确到达下游。# 从指定 Savepoint 恢复作业 flink run -s file:///data/flink/sp/savepoint-xxxx \ -c com.xxx.job.DmSyncJob \ ./dm-sync-job.jar-s 参数指向 Savepoint 路径恢复前提是作业代码逻辑和算子 ID 没有变化。如果改了连接器版本或重组了算子恢复可能报状态不匹配这时只能退回全量同步。所以我的习惯是连接器升级前先做一次全量同步演练避免线上恢复时才暴露兼容问题。存量增量衔接要重点验证 initial 模式下快照阶段产生的增量数据不丢。连接器的做法是先记位点再做全量快照快照执行期间业务库的写操作全部记录在已保存的位点之后快照完成后会从该位点继续消费。验证方法是在快照进行中持续往源表插入数据等任务跑完对比源表和目标表的总行数和最大更新时间两边一致才算过关。5.2 性能观察与上线前检查清单性能观察主要看两个指标currentFetchEventTimeLag 表示日志读取到当前时间的延迟currentEmitEventTimeLag 表示事件从读取到输出的延迟。前者高说明日志解析堆积后者高说明下游写入或序列化是瓶颈。单表 CDC 任务的 Source 通常只有一个并行度日志解析线程是单线程模型期望通过增加并行度提升吞吐在本场景意义不大更有效的做法是优化下游批量写入参数。从那以后我每次上线达梦同步任务之前都强制走一遍三件事确认归档和补充日志状态用一条 UPDATE 验证非主键列镜像完整再跑一次 cancel 后恢复的断点续传演练。这套流程帮我挡掉过好几次线上翻车也把日志同步从玄学变成了可预期的工程交付。希望帮到你如果你手上正好有达梦实例把配套连接器和配置模板跑一遍比反复看文档更直观。本文还有配套的精品资源点击获取