
1. 项目背景与整体设计思路1.1 为什么需要双向同步先说结论达梦DMHSDM High Availability Synchronization其实不是一套单纯的同步工具而是一套基于日志解析的实时数据同步框架。这次要做的项目是在一个既有Oracle又打算逐步引入达梦DM8的混合环境里把两套数据库之间的数据做成双向同步。说白了就是Oracle里改了数据DM8这边跟着变DM8里改了数据Oracle也跟着变。两边都能写两边都要一致。这类场景在实际生产里太常见了。比如企业正在从Oracle往达梦迁移但你不可能一天之内把所有业务全部切过去总要有一个“并行运行”的过渡期——核心交易系统还写在Oracle新的报表、分析系统已经跑在达梦上两边数据需要互相补充。再比如双活容灾架构两个机房各自有一套库平时分摊读写故障时互相接管这种设计也天然需要双向同步。很多人一听到“数据同步”就以为跟MySQL的主从复制差不多配个主从、丢个binlog就完事了。异构数据库之间的双向同步远比同构复制麻烦SQL方言不一样、数据类型映射不一样、事务处理机制不一样再加上双向之后还要防环路、防冲突整个复杂度是几何级上升的。这也是为什么这个项目值得单独拿出来复盘一遍。1.2 方案选型对比做异构数据库双向同步市面上的路子大概有这么几条基于应用层双写业务代码里同时写两个库。这个方案侵入性太强每个业务模块都要改代码事务一致性很难保证基本只适合特定的弱一致性场景。基于中间表/消息队列应用只写主库通过Canal、Debezium之类的工具把日志解析出来投递到MQ再由消费端写到目标库。这条路灵活但链路长、环节多监控和排障成本高而且消息队列本身还得自己运维。基于数据库原生日志同步达梦这边就是DMHS。它直接读源库的归档日志/联机日志把解析后的操作运送到目标库执行不侵入业务代码也不依赖MQ这类外部组件。最终选DMHS理由是它原生适配达梦对Oracle的日志解析做得也比较成熟而且一张图就能看清数据流向部署和运维链路都短。对生产系统来说多一个组件就多一分故障风险能少搭一层就少搭一层。1.3 整体架构与数据流向我这次搭建的环境参考了常见的两库两实例部署模式可以简化为源库AOracle 11g承载存量核心交易保持在线运行。目标库B达梦DM8承载新上线的查询/分析类业务已完成了存量数据的全量导入。DMHS正向链路Oracle A → DM8 B实时把A上的增量变更应用到B。DMHS反向链路DM8 B → Oracle A实时把B上的增量变更应用到A。两条链路分别由各自的DMHS实例负责互不干扰。搭建顺序上我强烈建议先做单向等一正一反两条单向都跑稳了再叠加成双向。一上来就配双向出了问题你根本分不清是正向链路坏了还是反向链路坏了。2. 部署环境准备与基础检查2.1 版本选型与兼容性这一节最容易翻车我吃了不少亏才摸出经验。DMHS不是不分版本乱配的它跟达梦数据库版本、操作系统位数、JDK版本都有对应关系。你拿着一个DMHS V4.2的安装包去连DM8通常也能跑但日志解析的效率和一些新特性就用不上。参考这次环境的实际情况合理的选择是一套与DM8同时期发布的DMHS版本配套的JDK环境统一用64位JDK8。原因很简单JDK8是企业级应用中最普及、问题最少的一个版本DMHS的图形化控制台和部分脚本工具依赖它版本过高或过低都会冒出一堆莫名其妙的问题。另外要特别注意操作系统用户权限。DMHS的解析进程需要读取Oracle的归档日志、达梦的归档日志还要写自己的缓存文件如果启动用户对这些目录没有读写权限典型的表现就是同步任务能起来但一有日志产生就报“file not found”或“permission denied”。2.2 环境基础检查清单我整理了一份部署前的基础检查清单照着走一遍能省掉后续80%的排障时间检查项要求参考命令/工具操作系统版本和位数与DMHS安装包匹配生产强烈建议Linux x64uname -m数据库版本Oracle 10g/11g/12c/19c达梦DM8各库自带版本查询SQL归档日志源库必须开启归档模式且归档目录可用空间充足archive log listOracle达梦查dm.ini中的ARCH_INI网络连通性两库之间、DMHS实例与两库之间TCP互通telnet IP 端口JDK版本64位JDK8java -version数据库授权DMHS需要单独授权文件达梦数据库本身也需要合法License达梦的dm.key/license文件时区设置尽量保持两库和DMHS主机时区一致避免时间字段错位timedatectl这里面“归档日志”是最容易被忽略的一项。Oracle不开归档DMHS基本就废了一半——它解析不到完整的重做记录达梦这边也一样不开启归档的话很多配置在初始化阶段就直接报错。我见过有人辛辛苦苦配完一整套结果启动同步时报“no archive log mode”不得不回过头去改数据库配置白白折腾半天。2.3 源端准备对比两边数据库基础信息在真正配置DMHS之前先把两边的数据库核对一遍最好做一个《两库基础信息对照表》。内容包括数据库版本、字符集、排序规则需要同步的业务库名和Schema名同步用户的账号权限至少具备读取日志/归档的权限两边的表结构差异字段名大小写、字段类型、自增列实现方式举个例子。Oracle里常用的NUMBER(10)字段到了达梦里可能映射为INT或DECIMALOracle的VARCHAR2默认用BYTE语义计长度达梦默认用CHARACTER语义同步的时候如果长度计算方式不一样字符串稍微长一点就可能溢出来。这些坑不在配置DMHS的时候冒出来而在你开始压测大字段数据时集中爆发。所以我的经验是先跑一次工具自带的“结构比对”能力如果没有就手工导出两边的建表语句放在一起diff。别嫌麻烦这一步省了后面麻烦更大。3. 初始化数据全量迁移与一致性校验3.1 全量迁移工具选择双向同步解决的是增量问题但前提是存量数据已经一致。你不可能让一条DML操作在一个两头都缺数据的表上同步出正确结果。全量迁移阶段的选择可以简单归纳为三种主流方式用达梦官方提供的迁移工具DTSDM Data Transfer直接连接Oracle和DM8走数据抽取和装载。优点是图形化、简单直接跟达梦兼容性最好适合单次性的全量搬移。用DMHS自带的“数据初始化”模块由DMHS在启动同步前自动完成基础数据的追平。这种方式的好处是跟增量流程无缝衔接但配置要求高执行耗时也更长。用第三方ETL工具。灵活但会造成额外的依赖一般不建议在从Oracle迁移到达梦这种场景里引入。实际项目中很多人喜欢用DTS但我想提醒一句DTS做全量迁移时默认的事务粒度比较粗如果你的表是真千万级以上的大表中途失败续跑会非常痛苦。更稳妥的做法是把大表拆分按照主键范围分批抽取。3.2 迁移执行与一致性校验全量迁移做完不等于数据没问题校验这一步不能省。常见做法是两边各出一份统计“指纹”条数校验SELECT COUNT(*) 按表比对。关键字段SUM校验例如对金额、数量类的数值字段做SUM。全表Hash校验生产数据量大时可以对整表做聚合哈希比较摘要值。我自己踩过的坑是数字精度。Oracle的NUMBER类型在迁移到达梦时如果目标字段定义成DECIMAL(10,2)而源数据有超过两位的小数DTS工具默认会截断或者报错。数据校验的时候COUNT和SUM全都对得上但逐行核对明细才发现有一批金额被静默四舍五入了。所以迁移之前必须检查设计文档里的字段精度定义而不是完全相信工具的默认映射。校验通过之后再执行两个重要操作记录一个全量快照时间点作为增量同步的起始位点。在目标库重新收集统计信息让查询优化器拿到准确的基数估计。这两步看起来不起眼但直接影响后续DMHS同步起点的准确性和目标库查询性能。尤其第二条从Oracle导入DM8后如果不收集统计信息查询计划大概率不是最优的。4. 核心环节实现DMHS双向同步配置4.1 DMHS基础概念与组件认知先花点篇幅把DMHS里的几个核心概念捋清楚。DMHS的部署逻辑很简单DMHS服务端dmhs_server核心组件负责连接源库和目标库执行日志解析与装载。DMHS控制台dmhs_console管理客户端可以启动/停止同步任务、查看同步状态、配置管理。同步任务Job一组配置里定义的同步链路包括源端连接信息、目标端连接信息、同步对象映射、过滤规则等。缓存文件.log/.datDMHS解析日志后先在本地落盘缓存再按事务顺序投递到目标库确保异常断点场景下不丢数据。你可以把DMHS想象成一个快递中转站源库的日志是发货方的货物DMHS负责拆包、重新打包、运到目标库签收。货物不会凭空消失它一定会经过中转站的缓存仓库这个设计保证了可靠投递。4.2 配置正向链路DM8订阅Oracle增量以下是我在Linux环境中配置正向链路的关键步骤和配置要点。由于不同版本DMHS的配置字段略有差异这里给出的是通用化的核心配置思路以dmhs.hs主配置文件为参考。第一步编辑DMHS的主配置文件设置全局参数?xml version1.0 encodingUTF-8? dmhs server iddmhs_ora2dm/id ip192.168.10.10/ip port5345/port userdmhs_user/user passworddmhs_pwd/password /server sync source typeORACLE/type host192.168.10.20/host port1521/port usersync_user/user passwordsync_pwd/password log_path/oracle/archive/log_path parse_threads2/parse_threads /source target typeDM8/type host192.168.10.30/host port5236/port userdm_user/user passworddm_pwd/password /target map item source_schemaSCOTT/source_schema target_schemaDMUSER/target_schema /item /map /sync /dmhs这段配置的核心逻辑是source段定好了Oracle连接方式和归档日志路径target段定好了DM8的连接方式map段则负责把Oracle的SCOTT用户对象映射到DM8的DMUSER用户下。这里有几个很容易出错的地方source的user/password不是普通的DBA账号而是至少具备读取归档日志权限的同步账号。如果权限不够DMHS解析进程会报ORA错误然后整个同步任务像心电图一样一抖一抖地重启。log_path要精确指向Oracle的归档日志目录。如果Oracle配置了多路归档DMHS会按顺序扫描路径填错就会一直处于“wait archive”状态。parse_threads并不是越大越好。解析线程数跟主机CPU核数和日志产生速度强相关一般从2到4开始压测我见过有人拍脑袋配了16结果CPU打满、同步延迟反而更高。第二步在DMHS控制台执行初始化命令start exec这个命令会启动同步任务。首次执行时它会从配置里记录的最早可用日志位点开始解析把积压的历史归档追平然后进入实时监听状态。如果你想指定从某个SCN/时间点开始可以先把同步任务暂停然后在控制台里设置启动位点再执行start。第三步验证状态list status正常状态下你会看到每个同步任务有明确的RUNNING标识并且有同步延迟时间统计。延迟如果一直上涨优先检查目标库的装载线程是否被大事务卡住。4.3 配置反向链路Oracle订阅DM8增量反向链路的核心配置跟正向几乎一致只是source和target互换源端是DM8目标端是Oracle。但有几个点需要单独注意。达梦作为源端时它不需要像Oracle那样依赖外部的归档日志目录而是读取自己的归档日志文件归档路径配置在dm.ini中。反向链路的配置关键是source段的log_path一定要指向达梦的ARCHIVE目录同时确认DMHS的执行用户对这个目录有可读权限。另外反向同步时目标Oracle端建议开启并行装载。Oracle装载大量小事务时如果串行执行性能很差。DMHS里可以设置装载线程数合理配置为事务平均大小的函数。经验值单事务行数小于100行的场景装载线程配4到8大事务为主则配2到3避免并行冲突。反向链路配置完成后同样执行start exec和list status验证。4.4 双向叠加时的环路规避与冲突消解到这里才是整个项目最有挑战的部分。两套单向链路单独跑都正常一叠加成双向就会出现一个经典的环路问题A库更新一行数据 → DMHS正向同步到B库 → B库更新之后产生新的日志 → DMHS反向又把这条日志解析出来同步回A库 → A库再次更新 → 再同步过去……数据没变化但日志在两条链路之间无限循环。解决思路有两个层次DMHS内置过滤配置中增加“忽略本机写入”规则。通俗讲就是每条同步任务只关心“外部来的数据”对“自己发出去的数据”不感兴趣。实际常用的办法是给每条同步链路打标记比如在两侧的同步用户下增加一个会话属性或触发器标记DMHS解析时识别到这个标记就跳过。业务层面冲突消解两边同时对同一条数据发起了修改这就要定义冲突仲裁规则。生产上一般按“时间戳优先”或“站点优先级”处理。配置扩展规则指定某个表以哪一端的修改为准哪一端发生冲突就覆盖或丢弃。我强烈建议在一开始就设计好互斥写规则不要让双向真正变成两边自由写。最常见的生产落地方式是按业务模块划分读写归属比如订单表只在Oracle写商品表只在DM8写两边都能查全量数据但各自只主动改自己的核心数据。这样双向同步90%的冲突问题在架构层面就直接消解了剩下的10%靠DMHS规则兜底。4.5 参数调优与压力验证双向同步部署完成之后紧接着就是压测验证。我一般先做小数据量的DML测试确认增删改查四类操作都能正确同步然后把压力逐步提高从每秒30条事务加到每秒500条甚至更高持续跑30分钟以上。压测期间重点关注三个指标同步延迟单位时间内目标库跟源库的滞后时间正常应该稳定在小幅抖动范围。两库数据一致性抽样对比最大主键、最新更新时间、条数统计确认没有漏数据、多发数据。DMHS进程内存和磁盘占用缓存文件不能无限增长如果发现缓存目录持续暴涨多半是消费端跟不上比如目标库的装载线程数量太少。压测过程中如果发现延迟显著上升第一时间看目标库是否有大事务、大批量UPDATE造成的行锁等待。我遇到过一次业务侧执行了一条UPDATE影响百万行正向同步卡了快20分钟源库日志不断堆积DMHS缓存文件几乎占满磁盘。解决方案是给同步任务增加大事务拆分参数让DMHS在解析时自动把超过阈值的大事务拆成多个小事务装载。5. 常见问题与排查技巧实录5.1 连接报错用户名或密码错误 (-2501)有人用Navicat连达梦时报“[HY000] 用户名或密码错误 (-2501)”第一反应就是改密码但改了还是报错。这个错误码很典型它不一定代表密码真的错了更多时候是客户端驱动跟服务端的加密方式不匹配或者是连接串里的schema/用户信息写错。如果你也在用Navicat连接达梦注意两点需要下载达梦官方提供的JDBC驱动包DmJdbcDriver18.jar在Navicat的“驱动管理器”里手动注册然后把连接方式选成“达梦”专用模式。直接拿通用JDBC方式连达梦很容易踩到驱动不兼容的坑。用户名如果写的是大写、小写跟实际创建用户不一致或者密码里带了特殊字符但没有转义也会被服务端拒绝。确认连接串后可以先在达梦自带的工具disql里用同一账号试一次如果在disql能连上而Navicat连不上问题基本就在驱动配置。如果是在DMHS配置里遇到类似的连接错误就检查source/target的用户权限是否足够而不是光看密码对不对。5.2 同步任务启动失败一直处于Wait状态这个我看得太多了。DMHS任务启动后一直显示“wait exec”或“wait archive”不代表配置错误也不代表网络不通多半是它找不到可以从哪个日志位点开始解析。排查顺序建议用list exec指令查看具体状态码和日志提示。去DMHS的日志文件目录看启动过程的详细记录通常会有明确的提示信息比如“archive log not found”或“scn out of range”。检查源库的归档日志是否被清理过。DMHS记录的最后同步位点如果对应的是一个已经被删除的归档文件它会一直等待直到你主动重置位点或指定新的起点。确认归档目录路径跟实际环境一致尤其注意软链接的情况DMHS对带软链接的目录偶尔会误判。遇到这类问题别急着重启服务先搞清楚位点状态否则重启十次也是白搭。5.3 数据不一致时区、序列、大字段三座大山同步链路不报错不等于数据完全一致。我总结过三个最容易在异构双向同步中“阴人”的方面时区Oracle的TIMESTAMP WITH TIME ZONE到了达梦如果两边时区设置不一样显示上会差几个小时。这类问题靠肉眼很难发现压测时一定要专门构造跨时区数据。序列/自增列Oracle用SEQUENCE实现自增达梦用IDENTITY或序列初始化数据时如果不提前设置好当前值和步长两边生成的主键很快会互撞。比如Oracle已经生成到1000达梦还从1开始双向同步后马上主键冲突。大字段和特殊字符CLOB/BLOB数据在日志解析时如果超过一定阈值某些版本会截断或转码异常。建议压测时专门准备一批超过1MB的大字段数据跑一遍。通用排查手法是用两张对比SQL分别取两边的数据指纹比如对每张同步表计算COUNT(*)、SUM(关键数值列)、MAX(时间列)三个维度都一致了基本可以判定同步正常。如果对不上就缩小时间窗口逐条比对。5.4 达梦License与KEY替换注意事项部署过程中还有一个容易忽略但影响很大的细节达梦数据库的License。如果达梦装了试用版License到期了DMHS同步任务会突然中断现象是目标库连接失败或写入报错但DMHS进程自己还是活着的。这种“假存活”非常坑人。替换License文件dm.key后最好重启达梦实例让新授权生效同时确认DMHS的同步账号仍然有正常登录权限。我在项目里遇到过一次性替换了好几个节点的License结果漏了一台导致同步任务在那台机器上反复重启排查了半天才发现是授权过期。6. 双向同步的后续扩展与运维建议6.1 从“双向互备”到读写分离改造双向同步跑稳之后业务架构还可以再往前走一步。你完全可以把达梦这一端变成纯粹的“只读分析库”Oracle仍然承担全部写入这样双向同步退化为单向同步冲突风险归零运维压力小得多。等到达梦上所有业务完全成熟再把写入流量也切过来那时候再做一次反向切换就是一套标准的“去O”演进路径。我在实际操作中的体会是双向同步只是手段不是目标。很多团队看到“双向”两个字觉得厉害非要两边都能写才满意。但从运维视角看每一对“可写”的双向关系都意味着潜在的冲突和无限循环风险。如果业务上不是真的有“两边同时写”的需求强烈建议先用单向同步顶着让目标库保持只读。6.2 监控与告警的落地建议同步工具最怕“人在工位链路已挂”而一无所知。DMHS本身有控制台查询命令但生产上最好把这几个关键指标接入到统一的监控平台DMHS进程存活状态同步延迟时间超过阈值就告警缓存目录磁盘使用率接近阈值提前预警源库归档日志目录空间这个非常关键一旦写满DMHS和数据库都会受牵连6.3 切换演练与回退预案最后再多说一句回退方案。双向同步搭建完成不是终点真正的终点是你敢在出故障时一刀切下去——能切过去还能切回来。建议项目上线前做至少两次切换演练一次是模拟Oracle端故障把流量全部引导到达梦另一次是模拟达梦端故障确认反向链路能撑住写回Oracle。每次演练都要记录具体切换时间、数据差异、业务受影响窗口这些数据才是后续做容量规划和SLA承诺的底气。我在实际部署中练出来的一个习惯是每次变更同步配置之前先备份dmhs.hs和对应数据库的登录账号信息变更后立即执行一个自定义的“数据同步快速体检”脚本五分钟左右就能判断出链路是否健康。这个习惯帮我挡掉了好几次“改配置一时爽、半夜出事故”的悲剧你可以直接借鉴。