OGG Replicat从非集成到协同模式切换实战:线程映射与性能优化 做OGG运维这些年遇到最多的改造需求之一就是把应用进程Replicat从非集成模式nonintegrated切到协同模式coordinated。十套同步里总有那么一两套一开始为了省事用单进程硬扛等数据量上来发现单个Replicat追不上增量了目标端延迟从几分钟飙到几小时于是就开始讨论要不要把模式换掉。这篇文章把我实际做过的改造路径、踩坑点还有那些文档里没写清楚但很关键的小细节完整复盘一遍给正在评估类似改造的读者一个可参考的落地思路。1. 为什么要把非集成模式切成协同模式模式差异与适用场景1.1 三种 Replicat 模式的关系别把协同和集成搞混先说清楚概念。OGG 的 Replicat 大体上有三种存在形态经典/非集成模式Classic / nonintegrated、集成模式Integrated、协同模式Coordinated。很多文章把协同模式归到集成模式里这是一个常见的认知误区。其实协同模式本质上是经典模式的并行化变体它仍然是经典 SQL 应用方式只是把一个进程拆成多个线程并行干活。集成模式则是完全另一条路它需要目标端数据库能力参与通过入站服务器在数据库内部做事务分发和并行应用所以集成模式基本只适用于 Oracle 到 Oracle 的场景。协同模式不一样它不依赖目标库内部机制可以用于 Oracle 到异构数据库的复制路径这也是很多异构环境最后选择协同模式而不是集成模式的直接原因。这一段必须讲清楚因为转换前如果连模式和目标库机制都没搞明白后面很容易用错命令。很多人以为“非集成→协同”是把 Replicat 的某个开关改一下就好了其实协同模式是一个全新的进程拓扑它有一个协调器coordinator和多个工作线程worker thread需要通过ADD REPLICAT ... COORDINATED重新建立进程组而不是简单改个参数。1.2 非集成模式的瓶颈与协同模式的收益非集成模式最大的特点就是简单、直观、好排查。每个 Replicat 进程负责一组表的复制事务按源端提交顺序一条一条应用。这种模式在数据量不大、并发不高的阶段完全没有问题但到了一定规模单进程应用就成了明显的瓶颈。我见过最典型的情况是某个业务高峰期目标端延迟一路飙到两三个小时源端每秒事务量几千但目标端单个复制进程大概只能处理实时的一半不到越积越多最后只能靠扩容服务器硬扛但单进程又吃不满多核扩容效果很有限。协同模式解决的就是这个“单进程吃不掉并发、多核又用不上”的尴尬。它把整个复制组拆成多个线程每个线程可以负责不同的表理论上线程数接近表数量或者拆分的分区数时并行度可以做得非常高。对多表并行更新的 OLTP 场景改造后实时延迟能从小时级降到分钟级甚至秒级这个收益是立竿见影的。而且协同模式的应用方式仍然是标准 SQL也就是说目标端不需要像集成模式那样做额外的数据库配置对 DBA 来说排查方式也基本延续了经典模式的经验。但这里必须说清楚协同模式不是银弹。它的并行收益取决于表与事务的分布。如果一个事务里同时更新了多张表而这些表恰好被分配到了不同线程协调器就必须把这件事变成一个全局事务来做串行协调这个事务的并行度直接归零。所以后面我们会重点说线程映射怎么设计这是整个转换里最影响效果的一步。1.3 什么情况不适合做这次转换不是所有环境都适合从非集成切到协同。我自己在评估时通常会直接排除三类场景。第一类是业务里有大量跨表大事务比如一个批处理更新几十张表、单事务数据量几十万行以上这种事务在协同模式下反而会因为全局事务协调而变慢有时候甚至比单进程还差。第二类是目标端对事务提交顺序有强依赖比如下游系统要求严格按主键或时间顺序消费多线程并行提交可能打乱跨表提交顺序这种环境更适合维持单进程或用集成模式。第三类是 OGG 版本太旧协同模式是 12c 之后才正式支持的能力如果还在用老的 11g 版本很多参数和命令都不支持硬切风险很大属于要先升级再改造的情况。排除完不适合的场景之后如果确认要做转换接下来进入评估阶段。这一阶段做得越细后面切换那天就越少出幺蛾子。2. 转换前评估清单版本、表分布、DDL和回退方案2.1 版本与平台兼容性核对我遇到过不少人在现场直接开干结果第一步就卡住版本不支持协同模式。所以先花十分钟把版本对齐比什么都重要。需要核对的东西主要有几个层面。OGG软件版本协同 Replicat 在 12c 及以后的版本才逐步完善19c、21c 这些新版本用起来更顺。如果线上还跑着 11g建议先规划 OGG 版本升级而不是强行用老版本去做协同复制老版本既缺功能又缺补丁踩到 bug 很难受。源端和目标端数据库版本如果目标端是 Oracle通常对版本要求相对宽松如果目标端是非 Oracle比如 PostgreSQL、MySQL、SQL Server 这类要查官方兼容矩阵确认当前 OGG 构建是否支持目标端的协同复制。不是所有平台的 OGG 都支持协同模式我之前接手过一套异构目标环境文档上写的支持范围和实际版本对不上最后只能回退方案重来。还有一个小细节如果是分布式或 RAC 目标端要考虑客户端连接串、库内负载均衡这些因素对多线程同时连库的影响。有些调度工具或中间件会限制单用户的最大会话数多线程复制一启动连接数直接翻好几倍连接池不够就会报错。这一步看起来不起眼但实际现场最容易翻车。这块做完最好把结论写成一个简单的清单当前版本、目标版本、是否支持协同、已知限制有哪些。后面写变更方案的时候直接引用这份结论也方便团队评审。2.2 线程数量与表映射策略设计线程数量不是越大越好。很多人的第一反应是“我有 20 张表就开 20 个线程”结果启动后性能反而更差。原因在于每个线程都有自己的检查点、内存缓冲和日志句柄线程之间还有协调通信开销。线程太多协调器本身可能成为瓶颈。我一般的经验是先从 4 到 8 个线程起步跑一天看各线程负载分布再决定要不要加。这里有个例外如果核心表就那么三五张每张表更新都很大那么干脆按表分配一张表一个线程简单又高效。表映射策略是整个转换设计里最需要花心思的部分。原则只有一条尽可能让一个事务涉及的多个对象落在同一个线程上。举个例子订单表和订单明细表基本是同时更新的那这两张表必须放在同一个线程否则每笔订单都会触发全局事务协调并行度直接归零。反过来完全独立的模块比如库存表和用户表之间没有事务关联就可以放心分到不同线程。对于超大表还可以考虑把一张表按主键范围拆成多个片段分给不同线程。这种用法在非集成模式下完全做不到是协同模式带来的一个重要优势。拆分时要注意数据分布是否均匀比如按时间字段拆分老数据和新数据天然不均衡容易造成某个线程空闲、某个线程打满。评估阶段最好先从数据库统计信息里拿一张“高频更新表及事务关联关系”清单把表和表的更新频次、是否同事务更新都列出来再据此做线程映射。这步做得越细后面调优成本越低。2.3 DDL、序列与批处理事务的影响面排查协同模式对 DML 的处理很成熟但 DDL 和序列这两块需要有心理准备。DDL 复制在协同模式下面并不是完全不能用而是处理方式变了。很多 DDL 需要靠协调线程去串行应用而且部分 DDL 类型在协同模式下兼容性不如经典模式。比如某些在线重定义操作、跨 schema 的 DDL可能会在应用阶段报错。如果业务里 DDL 很频繁我建议在转换前测试充分必要时对 DDL 单独开一条复制链路而不是硬塞进同一个协同复制组。序列的复制就更有意思了。OGG 复制 sequence 本来就需要单独配置在协同模式下更要小心。因为 sequence 变更本质上有全局顺序要求多线程并行复制容易把序列值搞乱。我的做法是转换前把序列复制和表数据复制彻底分开序列继续用一套独立任务来同步表数据走协同组两边互不干扰。还有一类容易被忽略的是批处理大事务。如果源端每天有几个小时跑批批处理事务单个就能达到几十万行甚至上百万行这种事务在协同组里会产生明显的“压队”效应。评估时最好看一下历史 AWR 或数据库日志里大事务的出现频率、发生时段、涉及的表集合。如果批处理事务太密集要么调整 batch 脚本把大事务拆成小事务要么从业务层面错峰否则协同模式的收益会大打折扣。2.4 停机窗口、验证脚本和回退预案模式转换本质上是一个重建 Replicat 进程的过程绝大多数方案都需要停止旧的复制进程。虽然可以做到短停但很难做到完全在线无感。所以提前申请一个停机窗口是必要的。窗口大小取决于 trail 积压量和目标端回放速度我一般建议至少预留 30 到 60 分钟其中真正切换操作可能只需要 10 分钟剩下的时间都留给验证和异常回退。验证脚本要提前准备好不能到现场现想。我的常用验证清单包括源端和目标端核心表行数比对、按主键随机抽样比对、最近一小时变更数据的时间戳比对、目标端告警日志检查和 OGG 自身日志检查。如果是异构数据库没有现成的比对工具就要提前写好基于时间戳和主键的校验 SQL或者准备一个简单的比对脚本。回退预案比转换本身更重要。最稳妥的方式是把旧的 Replicat 参数文件完整备份一份把旧进程的检查点位置记录到变更记录里旧进程的进程目录和检查点文件先不急着清理。万一协同模式启动后问题无法快速定位可以把旧进程加回来从刚才记录的位置继续跑。虽然听起来是“开倒车”但总比数据不一致强得多。3. 从旧进程到协同复制进程的实操步骤3.1 记录旧进程的检查点信息这一步是整个转换的基础也是最容易被忽视的一步。很多人在现场手忙脚乱就是因为没有想过删掉旧进程之后新的协同进程到底从哪里开始读 trail。答案就藏在旧进程的检查点信息里。操作前先确保旧 Replicat 已经追平到目标状态或者至少接受一个明确的断点位置然后执行下面的命令拿到详细检查点GGSCI STOP REPLICAT REP1 GGSCI INFO REPLICAT REP1, DETAIL输出里重点看Log Read Checkpoint File和Current Read Position两行。前者告诉你当前在读取哪个 trail 文件后者告诉你具体的序列号和 RBA 偏移量。举个例子输出可能是这样的Log Read Checkpoint File ./dirdat/aa0000010 Current Read Position: Seqno 10, RBA 2837564这里Seqno 10和RBA 2837564就是后面新建进程时需要指定的启动位置。我习惯顺手把EDIT PARAMS REP1的完整参数文件内容也复制一份存成.bak文件连同检查点信息一起存到变更记录里。别天真地以为参数文件后面能随时找回来实际现场一紧张很容易删错。注意删除旧进程后旧的检查点文件可能被一并清理。所以记录检查点信息这一步必须在DELETE REPLICAT之前完成最好把输出保存成文本文件而不是仅仅看一眼屏幕。3.2 配置新协同 Replicat 参数与线程映射拿到旧参数后开始做新版参数文件。这里不是把旧的参数抄一遍就行而是要加入线程映射。下面是一个典型的参数文件骨架REPLICAT REP1 USERIDALIAS ogg_admin ASSUMETARGETDEFS REPERROR DEFAULT, ABEND MAP sales.orders, TARGET sales.orders, THREAD (T1); MAP sales.order_items, TARGET sales.order_items, THREAD (T1); MAP sales.products, TARGET sales.products, THREAD (T2); MAP sales.inventory, TARGET sales.inventory, THREAD (T2); MAP sales.*, TARGET sales.*;第一条 MAP 把订单表和订单明细表放到了同一个线程 T1这是因为我评估时发现这两张表总是出现在同一个事务里。第二条 MAP 把产品表和库存表放到了线程 T2这两个模块的事务相对独立。最后的通配 MAP 负责兜底把其他没有显式指定的表自动分给剩余线程。这里提醒一句不同版本的协同 Replicat 对THREAD (T1)这种写法的解析可能有细微差异有的版本用THREADS (T1)有的版本还支持按范围拆分时配合KEYCOLS使用。配置前一定以当前版本的官方语法参考为准。我在现场见过版本升级后旧参数直接起不来的情况就是因为语法变了。参数里还有几个需要斟酌的点。如果原本使用了HANDLECOLLISIONS在新建进程启动阶段可以先保留用来吸收可能出现的少量主键冲突追平之后一定要去掉再重启否则它会持续掩盖真实的数据问题。另外BATCHSQL这类和批处理相关的参数在协同模式下的行为可能被忽略或受限建议对照版本手册确认不要凭经验一股脑全写上。3.3 使用指定检查点创建协同 Replicat参数文件准备好之后就可以用ADD REPLICAT创建协同复制组了。核心命令长这样GGSCI ADD REPLICAT REP1, COORDINATED, THREADS 4, EXTTRAIL ./dirdat/aa, EXTSEQNO 10, EXTRBA 2837564这条命令的要点是COORDINATED关键字表明创建的是协同模式进程组THREADS 4指定初始线程数为 4EXTTRAIL ./dirdat/aa告诉进程去读哪个 trailEXTSEQNO 10和EXTRBA 2837564是从刚才旧进程检查点里拿到的位置也就是新的协同进程要从这个位置接着往后读。这里有个容易踩的坑EXTSEQNO和EXTRBA必须严格匹配当前 trail 文件里真实存在的记录位置不能凭记忆大概填。如果 RBA 填得比文件实际内容还大启动时进程会一直卡在“定位 trail 记录”的步骤看起来像是没响应实际是在等待一个根本不存在的记录位置。万一填错了小版本可能还能通过调整位置纠正大版本直接就报错了。创建进程时会要求输入目标库的用户名和密码按实际情况填。如果目标库启用了密码加密要确保GGSCI客户端能正常连库否则后面启动会很折腾。创建完成后不要急着启动先做一次INFO REPLICAT REP1确认进程类型已经是COORDINATED线程数量也符合预期。确认无误后再进入启动阶段。如果你对旧进程的停止位置有信心这里其实已经完成了 80% 的操作。3.4 启动、追平与差异校验启动命令很简单GGSCI START REPLICAT REP1启动后立刻看状态GGSCI INFO REPLICAT REP1, THREADS正常情况下会看到协调器和各线程的状态。如果是RUNNING再观察一段时间重点看各线程的Lag趋势。如果 lag 在稳定下降说明线程划分基本合理如果某个线程 lag 持续增长、其他线程却很空闲说明映射策略不够好需要调整。追平之后要做数据校验。我的习惯是先看 OGG 自身统计GGSCI STATS REPLICAT REP1, THREADS这会输出每个线程应用的总记录数。然后启动前面准备好的比对脚本抽查几张核心表源端和目标端是否一致。如果出现主键冲突先分析是检查点位置偏差造成的重复还是源端数据本身就有问题。如果是重复应用可以临时让进程带上HANDLECOLLISIONS跑一小段把冲突吸收掉追平后再去掉参数重启一次恢复正常应用。整个切换过程中最怕的就是盲目追求“不丢一条”。宁可慢一点把检查和验证做扎实也不要因为赶时间把一个已经跑偏的复制组放到线上。4. 转换后的运维差异与高频问题排查4.1 线程负载不均的表现与重新分桶协同模式上线后最常见的问题就是线程负载不均。典型现象是INFO REPLICAT REP1, THREADS里一个线程的Lag持续增长其他三个线程基本是零延迟。这种情况基本可以断定是表映射设计出了问题最核心的几张表被塞到了同一个线程里。我之前处理过一个场景源端有一张大表更新量占全库的 70%日常 DML 又都集中在那几个热分区上。当时按表分配线程这张大表独占一个线程结果这个线程成了绝对的短板。后来改成按主键范围拆成三个片段分给三个线程并行整体延迟立刻下来了。调整线程映射不需要重建整个进程只需要修改参数文件里对应 MAP 的THREAD或拆分范围然后重启 Replicat 生效。但这里要特别提醒修改映射后已经分配给旧线程的 trail 位置和检查点会有一个重新分配的过程可能出现短时间内的重复或延迟一定要在业务低峰期操作操作完立刻跑一次校验。4.2 全局事务导致并行失效的识别线程负载不均还有一种隐蔽的表现所有线程看起来都在运行但整体吞吐就是上不去延迟降不到理想状态。这时候要怀疑是不是全局事务太多。全局事务的典型特征是一个事务里的操作涉及多个线程对应的表协调器必须保证这些线程之间的事务顺序和原子性。日志或者 report 文件里可能频繁出现类似“参与线程等待”“全局事务排队”的信息。遇到这种情况最好的办法不是加线程而是回到线程映射设计表把那些经常出现在同一事务里的表重新归纳尽量让每个事务的触达范围局限在单线程内。还有一种识别技巧观察源端事务的平均大小。如果源端平均事务都很大每个事务都涉及十几张表那协同模式先天就不占优势。这种场景要么推动业务拆事务要么就得接受现状不要抱有不切实际的性能预期。4.3 DDL 和序列同步的几个隐藏坑转换完成后如果原来的复制链路里有 DDL 复制一定要在第一个业务 DDL 到来之前主动测试。协同模式下 DDL 应用路径和经典模式不一样我见过最典型的报错是“协调器无法将 DDL 分配给线程”或者 DDL 应用成功后某些对象上的后续 DML 映射失效。规避办法是把 DDL 复制拆到独立的复制通道或者临时手动介入在源端执行 DDL 之前先暂停协同 Replicat等 DDL 在两边都完成后再从一致的检查点恢复。虽然烦但比 DDL 复制出错导致整个复制组崩溃要可控得多。序列同步的坑更隐蔽。协同模式下如果序列和表混在一个复制组里很容易出现序列值不一致因为序列的生成是全局递增的多线程并行应用会破坏它的单调性。我个人的习惯是转换前就把序列复制单独拆出去用专门的序列复制任务维护绝不让序列和 DML 混在协同复制组里。4.4 监控指标和工作交接注意事项上线协同模式之后监控逻辑也要跟着变。以前看一个INFO ALL里的 lag 就够了现在每张表可能分布在不同线程上整体 lag 正常不代表某个线程已经积压成山。建议把INFO REPLICAT REP1, THREADS的输出采集到监控平台按线程分别告警。还有一个小技巧协同模式每个线程有独立的 checkpoint日常备份和恢复演练时要特别注意线程目录和文件能否一起恢复。别只备份了协调器的主检查点把几个线程的检查点漏了真到恢复那一天会非常被动。我的习惯是在变更文档里明确列出所有线程目录和对应检查点文件的路径后续交接给其他同事时直接照单维护。最后说点个人体会做了几次非集成模式到协同模式的切换之后我最大的体会是这个改造的技术难度其实不高难的是对业务表关系的理解。线程映射表面上是配置活实际上是业务建模的缩影。哪些表一起更新、哪些事务跨模块、哪些大事务集中在什么时段这些信息如果调研清楚了协同模式的收益会非常可观如果只是机械地把表平均分到几个线程里很可能只是把一个瓶颈换成了另一个瓶颈。还有一个小建议切到协同模式后先让它稳定跑一到两周再考虑继续加线程。协同模式对表映射的调整很灵活但频繁调整会影响复制链路的稳定性不如先小步验证、再逐步优化。希望这份复盘能帮你少走一点弯路。