☰
OpenSearch CCR原理与实战:跨集群复制实现零停机数据迁移
2026/10/3 3:17:39 网站建设 项目流程

做过搜索集群迁移的同学应该都有同感:最头疼的不是写脚本,也不是调参数,而是在业务持续写入的情况下,怎么把数据完整、不丢、不重地搬到另一个集群。早年我惯用的套路是快照恢复加增量reindex,听着行,实操起来每次都要掐业务低峰期,还得处理索引映射、字段类型不一致的幺蛾子,整个人被折腾得没脾气。后来接触到OpenSearch自带的CCR(Cross-Cluster Replication,跨集群复制),才算是从“搬数据”的思维里跳出来,改成“复制数据流”的思路,迁移过程清爽了不止一个量级。这篇是我把CCR从原理到落地完整走了一遍之后的经验整理,先写上篇,讲清楚为什么选它、前置条件、怎么开启跨集群同步,以及如何验证数据一致性;至于故障转移、恢复、切换这些高级玩法,留到下篇再展开。

这篇指南适合正在做搜索集群迁移、架构升级或者数据中台底层数据整合的同学,无论你是刚接触OpenSearch的运维新人,还是已经折腾过reindex和快照的老手,只要集群版本在OpenSearch 1.0以上,都可以照着这套方案把CCR用起来。我会尽量把每一步背后的逻辑讲透,而不只是丢命令,毕竟迁移这事,真正坑人的从来不是不会敲命令,而是不知道命令为什么这么敲。

1. 为什么跨集群数据迁移要优先考虑CCR

1.1 传统迁移方案到底痛在哪里

做数据迁移,很多人第一反应是快照恢复或者reindex,这俩方案不是不能用,而是各有各的坑。快照恢复适合一次性全量搬迁,流程简单粗暴:在老集群打快照,传到新集群,恢复。问题在于,快照恢复的是某一个时间点的数据,从打快照到恢复完成这段时间内的新增数据是缺失的。你用快照做迁移,必须在恢复完成后手工追增量,如果业务写入量大,追增量的窗口期会非常难受。

reindex的问题就更隐蔽一些。它从远端集群读取数据再写入本地,写入过程会重新执行分词、doc_values构建、倒排索引构建,CPU、内存、IO开销都不小。索引越大,reindex越慢,而且reindex本身不解决映射冲突和分片结构调整的问题,很多时候你还得先手工创建好目标索引,再控制并发慢慢导。我在一次数据中台异构系统整合项目里,用reindex搬一个每天增长几十GB的日志索引,跑了将近两天,中间还因为bulk队列打满导致写入抖动,差点把在线业务拖垮。从那以后,但凡遇到持续写入的索引迁移,我都优先考虑能不能用CCR。

1.2 CCR的思路为什么更优雅

CCR的核心思路很直白:在目标集群创建一个“跟随索引”,这个索引会持续从源集群的“主索引”拉取操作日志,然后按顺序在本地重放。换句话说,它不是在“搬数据文件”,而是在“同步数据操作”。索引的mapping、setting会从主索引自动同步过来,写入过程是递增拉取translog里的操作记录,对源集群的查询和写入影响比reindex小得多。

打个比方,快照恢复等于把一本书复印一份带回家,复印期间书里新增的页码你得自己补;reindex等于重新抄写一本书,费时费力还容易抄错格式;CCR则像在你家开了一条自动抄写流水线,原作者每写一页,流水线就帮你抄一页,你随时打开新书都是最新的。对于需要持续写入的生产索引,这几乎是迁移期的标准答案。同步过程中,源集群完全不用停机,业务照常读写,目标集群的数据会以一个可控的延迟追平源集群。等到两个集群的数据基本一致时,再挑一个低峰期切换读写流量,迁移就结束了。

1.3 三种方案的选型对照

我不太喜欢直接给结论,因为方案选型跟具体场景强相关。这里把自己常用的选型判断标准整理成表,方便你对照自己的场景:

场景快照恢复reindexCCR
一次性全量迁移,业务可暂停适合可用可用,但偏重
持续写入的在线索引迁移需追增量,麻烦慢且影响性能最适合
多集群容灾/实时备份不适用不适用天然适合
索引结构需要同步手动处理手动处理自动同步
目标集群要改mapping/分片数恢复前可改可自定义受限,需提前规划

这是我自己实践中的经验总结。单次迁移、允许停写,快照恢复确实省事;但你要是想在零停机的情况下完成迁移,CCR就是最值得投入时间研究的方案。尤其现在不少团队在搭数据中台,搜索集群往往要同时向多个业务系统提供数据,CCR这种“源端无感、目标端持续追平”的复制方式,比在中间层写一堆同步脚本要干净得多。顺带说一句,很多数据迁移工具比如dm、或者是直接导入.sql文件,更多是针对关系型数据库的,对搜索集群这种文档型、倒排索引结构基本帮不上忙,搜索集群的迁移还是得靠集群自身的能力。

2. CCR核心机制与前置条件

2.1 必须搞懂的四个角色

CCR里有四个核心角色,很多人一开始搞混,我在刚开始接触时也绕了一阵。首先是Leader集群,也就是数据源所在的集群;其次是Leader索引,就是源集群里被复制的那个索引。然后是对应的Follower集群,也就是目标集群;最后是Follower索引,就是目标集群里跟着Leader索引走的那个本地副本。你可以简单理解成:Leader是母版,Follower是照着母版实时更新的复印件。

这里有一个需要特别留意的机制:Follower索引本身是只读的,你不能直接往它里面写数据。所有写入都来自复制通道,本地应用只能对它做查询。这个设计是为了保证复制的一致性,避免本地写入和源端操作冲突。如果你在目标集群想改数据,正确做法是先暂停复制或者切换成独立索引,而不是硬写。我见过有同事刚上手时习惯性地往Follower索引里塞测试数据,结果复制通道直接报错,排查了半天才发现问题出在自己手上。

2.2 数据是怎么从Leader复制到Follower的

我尽量把机制讲得通俗一点,不用太复杂的术语。Leader索引每次写入的时候,都会把操作记录到translog里,也就是事务日志。CCR的复制线程会定期从Follower集群发起请求,到Leader集群那边拉取translog里的新操作,然后把这些操作拿到Follower集群本地重放一遍。这个“定期”是非常频繁的,默认情况下几乎可以达到准实时的效果,一般延迟在秒级甚至毫秒级。

把这里再往深挖一层:OpenSearch在复制的时候还会复用一个叫“remote cluster”的基础设施,也就是远程集群连接。Follower集群需要先配置好对Leader集群的网络访问和连接信息,才能通过远程集群接口去拉数据。说白了,CCR是构建在远程集群连接之上的一层高级功能,你必须先打通两个集群之间的网络通路,复制通道才能真正工作。

还有一个细节值得注意:Follower索引的shard数与Leader索引必须一致,这是由复制机制决定的。CCR是按shard维度做复制的,Leader的每个主分片都会在Follower端对应一个分片来接收复制数据,shard数不一致会导致复制任务创建失败。所以你想做迁移的索引,如果打算在目标端调整分片数,要么提前规划好,在创建Leader索引时就要想清楚;要么就不要对Follower索引做分片数变更。这一点是CCR的硬限制,绕不过去。

2.3 版本、配置和权限:少一个都跑不起来

先说版本。OpenSearch从1.0版本开始内置了CCR能力,但建议你的Leader和Follower集群版本尽量保持一致,或者至少Follower不低于Leader。跨大版本做CCR我没试过,官方也不建议,毕竟translog的解析格式在不同版本之间可能变化,一旦不兼容,复制任务会反复报错。如果你是在做跨版本升级迁移,稳妥路线是先升级源集群,再开CCR,或者直接用快照+增量reindex的组合,不要硬上CCR。

再说集群配置。开启CCR需要在Follower集群的配置里加上远程集群连接。远程集群连接有两种配置方式:一种是在elasticsearch.yml(OpenSearch里对应opensearch.yml)里静态配置,一种是运行时通过API动态配置。静态配置要改配置文件然后重启集群,动态配置不用重启,推荐优先用动态方式。具体配置内容就是给远程集群起一个名字,然后指明它有哪些节点地址。

最后说权限。如果你的集群开了安全插件,比如Opensearch Security,那事情会多一些。Follower集群连接Leader集群需要有对应的权限,一般是在Follower端配置一个专门的用户,角色要包含remote_cluster_client之类的权限;在Leader端,则要允许Follower集群的连接请求通过认证。很多第一次配CCR的人在这里卡住,报错日志往上一翻全是认证失败,却死活找不到原因。我的建议是:在正式配置之前,先用curl或者kibana控制台确认两个集群之间的网络和认证都通,再去做后续操作,能省大量排查时间。

2.4 需要注意的网络与资源规划

CCR的本质是跨集群拉取数据,所以网络带宽会直接影响复制延迟。如果你的两个集群跨机房甚至跨地域,延迟和带宽都得认真评估。我在一个异地容灾项目里做过测试,带宽充足的情况下,复制延迟可以控制在1秒以内;但带宽被业务流量挤占时,延迟会飙升到几十秒甚至分钟级。建议在生产环境预留单独的复制通道带宽,或者至少用流量整形手段给CCR请求设置优先级。

资源方面,Follower集群需要预留足够的磁盘和内存来承接复制数据。一个常见的误区是只按当前数据量规划磁盘,忽略了translog在复制过程中的累积。如果Follower追不上Leader的写入速度,translog会一直积压,磁盘和内存占用都会快速上升。后续调优思路里我会讲怎么监控这个积压量,这里先提个醒:不要低估追平阶段对临时存储的消耗。

3. 实操:从零开启CCR跨集群复制

3.1 准备环境与网络连通性检查

动手之前,先列一个检查清单。两个集群的版本确认好,建议都升级到OpenSearch 2.x以上,我这边是用2.11完成的验证。两个集群的节点地址互相能访问,尤其是Follower集群的节点要能访问Leader集群的transport端口,默认是9300。如果你还在两个集群之间挂了安全组、防火墙,先把端口放通,再做连通性检查。

我习惯用telnet或者nc检查端口连通性,简单直接。比如在Follower集群的某个节点上执行:

nc -vz leader-node-1 9300

如果端口通了,再往下一步。不通的话,先查网络策略和防火墙规则,不要急着配CCR,不然一会儿连接超时或者认证失败的报错会让你怀疑人生。之前我处理过一个案例,明明安全组看起来都放通了,结果两个集群用的VPC网段有路由冲突,请求老是超时,最后定位到是网络路由表的问题,亏得提前做了连通性检查才没浪费时间。

3.2 在Follower集群配置远程集群连接

远程集群连接配置是CCR的前置条件。如果你的环境允许改配置并重启集群,可以在opensearch.yml里加:

cluster.remote.leader_cluster.seeds: ["leader-node-1:9300", "leader-node-2:9300"] cluster.remote.leader_cluster.mode: snapshot

其中leader_cluster是你给远程集群起的别名,后面创建Follower索引时要引用这个名字。mode建议保持snapshot模式,这是最常见的用法。

但多数生产环境不方便重启集群,那就用动态API方式配置,不需要重启,效果是一样的。在Follower集群执行:

PUT /_remote_cluster/leader_cluster { "mode": "snapshot", "seeds": ["leader-node-1:9300"] }

配置完成后,可以用下面的API确认连接状态:

GET /_remote_cluster/info

如果能看到远程集群的节点信息,说明连接已经建立。如果看不到节点信息,优先确认seeds地址对不对、网络端口通不通、认证是否通过。这里多说一句,远程集群连接名一旦被子序列化到后面创建的Follower索引配置里,想改名就得删掉复制任务重建,所以刚开始命名就想好,比如用leader-cluster-a这种明确的业务名,别用test1这种。

3.3 创建Follower索引并启动复制

连接配好之后,就可以创建Follower索引了。先在Leader集群确认一下你想复制的索引是什么名字,比如orders_v1。然后在Follower集群执行CCR的follow接口:

PUT /orders_v1_ccr/_ccr/follow { "remote_cluster": "leader_cluster", "leader_index": "orders_v1" }

这条命令的意思是:在Follower集群创建一个叫orders_v1_ccr的索引,让它从远程集群leader_cluster的orders_v1索引拉数据。创建过程中OpenSearch会自动同步Leader索引的mapping和setting,不需要你手工建索引,这是我特别喜欢CCR的一点。执行完成后,复制任务就启动了,从创建时刻开始,Leader索引上发生的写入操作都会持续同步过来。

如果你想复制的索引在Leader端已经存在,而且数据量比较大,CCR会先触发一次初始同步。这个过程本质上还是走translog,但对于已有数据,可以理解为复制通道会先把历史数据追平,再进入增量同步状态。初始同步期间,你可以通过监控接口观察复制的进度。这里我建议你创建Follower索引之前,先看一眼Leader索引有没有别名或者索引模板的依赖,因为别名、索引模板这些元数据不会通过CCR自动同步,得你手动处理,不然切换时可能出问题。

3.4 查看复制状态与延迟

创建完复制任务,第一件事是确认复制状态。最常用的接口是:

GET /_ccr/stats

这个接口返回的信息很丰富,重点关注几个字段:follow_status、leader_global_checkpoint、follower_global_checkpoint和read_operations。简单说,global_checkpoint就相当于复制到哪个位置了,Leader和Follower的checkpoint越接近,说明复制越实时。如果follower_global_checkpoint长期追不上leader_global_checkpoint,说明复制有积压。

我还习惯看每个分片的同步情况,用更细的接口:

GET /orders_v1_ccr/_ccr/info

这个能看到每个分片的leader_index、remote_cluster以及复制状态。字段里有个fatal_exception,如果出现非空值,说明复制任务挂了,需要排查。正常的复制状态应该是active,不会报错。我自己的习惯是配置一个简单的定时任务,每隔几分钟拉一次_ccr/stats,把checkpoint差值记录下来,这样可以直观看到复制延迟的趋势,一旦发现差值变大,就能提前处理,而不是等到切换时才发现数据没追平。

4. 数据一致性验证与常见问题排查

4.1 不写脚本也能验证同步质量

CCR同步跑起来之后,最担心的就是数据到底跟源端一不一致。验证数据一致性,最稳妥的办法是比对文档数和文档内容摘要。我推荐一个思路:先在Leader索引上执行_count统计文档数,再在Follower索引上执行同样的_count,两个数字相等是第一层验证。但文档数相同不代表内容一致,因为文档可能被更新或者删除过。

再深一层的验证是分片级别的校验。可以使用OpenSearch的_search接口,按_id排序,分段取回一批文档的_source,然后计算哈希值,两边的哈希结果做比对。这个方案不用额外插件,写个脚本就能实现。如果你的环境允许安装插件,还可以用开源的比对工具来做全量数据指纹比对,更省事。

我自己常用的一种方法是在迁移期间专门往Leader索引写一批带有特征字段的测试文档,比如在文档里加一个migration_test字段,值为当前时间戳。每隔几分钟从Follower索引读一次这批测试文档,确认它们能够出现在目标端、内容完整,并且删除日志索引场景下文档也能被正确删除。这相当于一套简单的“活体探测”,能及时发现同步链路的异常。生产实践里,这种方式比单纯看监控指标更能暴露实际问题。

4.2 复制中断:最典型的几个场景

复制中断是CCR运维里最常遇到的问题。我梳理了三个高频场景,你遇到了可以直接对照排查。

第一个是连接问题。Follower集群和Leader集群之间的网络波动,或者Leader集群节点重启导致远程集群连接失效,都可能让复制任务中断。这类问题通常可以在日志里看到连接超时或者remote cluster异常相关的报错。处理办法是先恢复网络,然后通过API重启复制任务。大部分情况下,OpenSearch会自动重试,重试几次后任务会重新恢复;如果长期恢复不了,就需要手动介入。

第二个是认证问题。如果两个集群之间配置的证书过期,或者Follower集群的用户角色变更,复制请求会被拒。这时报错日志里会出现类似security_exception的信息。解决方法是更新证书或重新配置用户权限,然后重启复制任务。这里强调一点:很多生产环境证书有有效期,CCR作为常驻后台任务,很容易因为证书到期而悄悄中断。建议把证书过期时间写进监控,提前更换,避免迁移窗口期才发现复制早就停了。

第三个是资源问题。Follower集群的磁盘满了、内存不足,或者Leader集群的translog被强制刷新,都可能导致复制失败。磁盘满的情况最常见,因为复制通道对存储的占用往往被低估。处理思路是上一节提到的,持续监控checkpoint积压,提前扩容磁盘或者清理无用索引。如果Leader端translog被冲刷,那部分历史操作可能无法拉取,任务会报出no_history之类的错误,这种情况比较麻烦,可能只能重建Follower索引。

4.3 常见问题速查表

整理一个速查表,方便你复制到自己的运维手册里。这里面的问题我在不同项目里基本都遇到过,排查思路也是验证过的。

现象可能原因快速处理
创建follow任务报错,提示remote cluster不存在Follower集群还没配置远程集群连接,或连接名写错检查_remote_cluster/info,确认连接名
创建follow任务报错,提示shard数量不一致Leader和Follower索引的分片数不一致删除目标索引,用CCR自动创建,不要手工指定分片数
复制延迟持续增大网络带宽不足,或Follower集群写入性能瓶颈检查带宽和分片级read操作耗时,做资源扩容
复制任务状态为fatal_exception认证失败、translog历史不可用、磁盘满等查看异常详情,先恢复资源/网络,再重启任务
Follower索引无法写入数据CCR的Follower索引默认只读确认是否确实需要写入,正常查询不受影响
数据追平后仍有少量不一致未监听索引别名、模板变更,或测试文档验证不全面补同步索引模板和别名,增加内容级校验

这张表只是快速定位用,实际问题往往要多角交叉。我一直强调一个观点:不要只信监控指标,要以实际的文档读取结果为准。监控指标能告诉你“任务活着”,但活着的任务也可能在源源不断地复制错误数据。

5. 从实操中总结的调优心得与后续扩展

5.1 复制延迟的调优方向

CCR默认参数在多数场景下表现不错,但如果你对延迟要求比较高,有几天可以直接调一调。核心参数是max_read_request_operation_count、max_write_request_operation_count和max_outstanding_read_requests、max_outstanding_write_requests。这些参数控制一次从Leader端拉取多少操作、Follower端能同时处理多少批次。理论上调大并发能降低延迟,但代价是增加内存和带宽消耗,所以不是越大越好。我一般从默认值开始,先看read_request_time_in_millis和write_request_time_in_millis的耗时占比,再决定是否调大并发。

另一个容易被忽视的调优点是在Leader端控制translog的保留策略。CCR要拉取的是translog里的操作记录,如果Leader端translog被过早清理,Follower可能来不及拉取,导致复制中断。建议在迁移期间适当增加translog保留时间或者设置较大一些的flush间隔,给Follower留足追赶窗口。这个操作不需要动业务索引,但能显著减少“复制断点找不到历史操作”的概率。

5.2 和现有数据迁移流程如何衔接

很多团队做数据迁移并不是单纯搬一个索引,而是借机会做架构调整,比如换集群、改分片、整合多套环境。CCR解决的是数据层面的持续同步,但你在实际项目中还会遇到索引模板、Ingest Pipeline、用户权限这些和索引强相关的依赖项。CCR不会帮你自动迁移这些,需要你单独导出并在目标集群重建。

以我之前参与的数据中台整合项目为例,业务方希望把多个业务系统的搜索数据统一到一套集群里。我当时的做法是:先用数据库迁移工具把源业务库的数据导入到中台的关系型存储,再通过ETL加工成目标索引格式;但历史存量数据好办,增量更新却很费劲。后来改成CCR做两层之间的搜索索引同步,存量加增量一次解决,而且不需要改业务代码。如果你的场景也是多源异构整合,建议把CCR放在数据加工链路的下游,专门负责“搜索索引级”的同步,不要试图用它替代数据库层面的数据迁移,工具各有边界,硬凑反而多出维护成本。

5.3 一个值得记住的实践技巧

最后分享一个我自己常用的技巧:在正式迁移前,先挑一个小体量、低优先级的索引做试点,完整走一遍“配置远程集群连接、创建Follower索引、观察同步、校验一致性、关闭同步”的流程。这样做有两个好处:一是能把团队里每个成员的手感和流程磨熟,避免正式操作时手忙脚乱;二是能提前暴露两个集群之间特有的问题,比如安全策略、网络路由、命名规范之类的坑,在试点阶段发现和修复的成本远低于生产切换时。

另外,复制任务停止以后,那个Follower索引会从只读状态变成普通索引吗?这里要注意:CCR关闭后,Follower索引依然是只读的,如果你希望把它当作独立的线上索引来承接读写,需要显式关闭索引的只读属性,并确认不再有复制通道引用它。我接触过不少同事在测试环境直接删除remote cluster配置,结果Follower索引被锁死,读写全部报错,就是因为少了这一步。做迁移收尾时,把“解除只读”写进checklist里,能少踩一个重复出现的坑。

CCR这个功能还有一个很多团队没充分利用的场景:把它当作跨集群容灾的标准复制通道,而不仅仅是在迁移时用一次。我在实际运维中体会很深的一点是,CCR一旦建立起来,它就是一个持续在跑的复制链路,你可以随时在Follower集群查看数据、跑报表、做测试,而不必担心影响Leader集群。这种“复制链路的复用价值”,比一次性迁移本身更值得珍惜。建议你在完成一次迁移后,不要把CCR相关配置拆掉,而是顺势把Follower集群作为读扩展节点或者灾备节点保留下来,让投入的配置成本持续产生价值。后续我还会写一篇下篇,专门讲如何基于CCR做故障转移、主从切换和回切,那部分才是CCR真正展现弹性的地方。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询