先聊个实际场景:你手上有两个 TongSearch 集群,一个在主中心承载线上写入,另一个在异地机房,平时只读、用于查询和灾备。业务量上来之后,主库压力越来越大,你想把一部分读流量切到异地机房,却发现两边数据根本对不上。或者更惨一点,主中心某台机器磁盘坏了,索引分片丢失,你想从异地集群把数据再拉回来,结果发现只能重新灌数据。这些问题的标准解法,就是跨集群复制(Cross-Cluster Replication,CCR)。TongSearch 作为兼容 Elasticsearch 生态的分布式搜索系统,把 CCR 作为核心高级特性内置了进来。
这篇内容我会完整拆解 TongSearch 跨集群复制的数据同步流程,从底层原理、配置参数、创建步骤到常见的坑,一次性讲清楚。适合正在部署多地多中心架构的运维、中间件负责人、后端开发,以及所有被“集群间数据同步”折磨过的人。
1. 内容整体设计与思路拆解
1.1 跨集群复制到底解决什么问题
先说需求,跨集群复制本质上解决的是“数据在多个集群之间如何保持一致”的问题,但它和普通的消息同步、ETL 同步不同,CCR 工作在一个更底层的维度——分片级别。
TongSearch 的索引由多个分片组成,每个分片底层是 Lucene 索引。CCR 做的事情,就是在两个集群之间建立“领导索引”和“跟随索引”的对应关系,让跟随索引的每个分片持续从领导索引的对应分片拉取操作日志,然后重放到本地。这个过程用户无感知,业务方就像操作一个普通索引一样去读跟随索引。
主要解决以下三类问题:
- 灾备:主集群整体故障时,从集群拥有完整的数据副本,可以随时切换读流量甚至升级为写集群。
- 读写分离:写入集中到主集群,查询分散到从集群,降低主集群压力。
- 就近访问:不同地域的用户访问最近机房的集群,减少跨机房 RT。
和基于主从复制实现的方案比,CCR 的最大特点就是“跨集群”。它不需要两个集群在同一个集群内,也不要求集群间有共享存储,只要求网络层面能互相访问。这个灵活性很关键,因为它把复制能力从“单集群内部”扩展到了“多集群架构”。
1.2 同步流程的底层原理
CCR 的核心机制,是跟随集群(Follower)主动从领导集群(Leader)拉数据,而不是领导集群主动推数据过来。
完整流程可以拆成两段:
第一段是初始同步。创建跟随索引时,底层会先做一次全量数据拷贝。TongSearch 会把领导索引的 Lucene 分片文件通过快照方式拉到跟随集群,生成分片副本。这个阶段的数据量取决于索引大小,如果索引很大,耗时可能很长。
第二段是增量同步。全量拷贝完成后,跟随集群会持续从领导集群拉取领导索引的 translog(事务日志),把增量操作重放到本地分片。每次重放之前,会先记录一个检查点(checkpoint),标记“我已经同步到哪条日志了”。下次拉取就从这个 checkpoint 往后继续。
这里有一个容易被忽略的细节:CCR 是“从跟随端拉取”,不是“领导端推送”。这意味着领导集群不需要额外配置任何推送逻辑,甚至不知道有跟随集群存在。这个设计带来的好处很实际——领导集群不会因为下游同步而增加性能负担,也避免了推模式下的复杂流量控制。
1.3 为什么是“从端主动拉”而不是“主端推送”
我见过不少人第一次接触 CCR 时会问:为什么不是主集群直接推给从集群,这样实时性不是更好?
答案是:推模式在实际运维中很难控。主集群一旦承担推送职责,就要同时管理所有下游集群的状态、速率、失败重试,下游集群的数量一变多,主集群的负担和复杂度都会上升。而拉模式把控制权完全放在从端,从集群根据自己的处理能力决定拉取速率,主集群只需要像对待普通读请求一样处理从集群的拉取请求。
这个设计还带来一个额外优势:网络策略简单。只需要从集群能够访问主集群,不需要主集群反向访问从集群,也不用开放额外的入站端口,安全策略可以收敛得很干净。
1.4 复制延迟的组成
说完了整体架构,再说延迟。CCR 的复制不是实时的,必然存在一个“从写入领导索引到在跟随索引可见”的时间差,主要由三部分组成:
- 轮询间隔:跟随集群周期性去领导集群拉取新日志的间隔。
- 网络传输耗时:日志数据从领导集群传到跟随集群的时间。
- 重放耗时:跟随集群把日志写入本地索引的时间。
理解延迟组成很重要,因为后面排查“为什么从集群数据一直追不上主集群”的问题时,基本上就是从这三个方向去定位。
2. 核心细节解析与实操要点
2.1 集群白名单配置
真正开始配置 CCR 之前,需要先确认 TongSearch 的集群配置里打开了远程集群访问的白名单。这一步容易漏,漏掉之后配置远程集群时会一直报连接失败。
在 TongSearch 的elasticsearch.yml中,需要设置:
cluster.remote.connect: true如果集群启用了安全认证,还需要配置跨集群访问的用户名密码等信息。另外,TongSearch 支持通过search.remote.connections_per_cluster控制每个远程集群的连接数,这个参数默认值一般够用,但如果两个集群之间索引数量较多,可以适当调大。
配置完成后需要重启集群生效,注意这一步务必要做,否则后面配置 remote cluster 即使返回成功,实际同步请求也会报错。
2.2 远程集群注册
远程集群注册使用的是动态配置,不需要重启集群。TongSearch 兼容 Elasticsearch 的上游 API,标准做法是通过_cluster/settings接口写入远程集群地址信息。
PUT /_cluster/settings { "persistent": { "cluster.remote.leader01.seeds": ["192.168.1.10:9300", "192.168.1.11:9300"] } }这里的leader01是远程集群别名,后续创建跟随索引时要用到。seeds写的是领导集群的 transport 端口地址,不是 HTTP 端口。
配置生效后,可以先用下面的命令验证远程集群是否已连接:
GET /_remote/info返回结果里会列出已配置的远程集群和节点信息。这一步实测下来非常有用,能第一时间确认网络连通性和集群命名是否对得上。
2.3 创建跟随索引的参数解读
远程集群注册好之后,就可以创建跟随索引了。创建跟随索引的请求格式如下:
PUT /logs_replica/_ccr/follow?wait_for_active_shards=1 { "remote_cluster": "leader01", "leader_index": "logs" }请求里面两个核心参数,remote_cluster指定远程集群别名,leader_index指定远程集群上的源索引名。跟随索引的名称就是请求路径中的logs_replica,可以自定义,不需要和领导索引一致。
这个接口还有一批可选参数用来控制复制行为和性能,这一块是优化数据同步流程的关键。
以下参数建议重点关注:
| 参数名 | 默认值 | 作用说明 |
|---|---|---|
| max_read_request_operation_count | 5120 | 单次从领导端拉取的最大操作数 |
| max_read_request_size | 32mb | 单次拉取的最大数据量 |
| max_outstanding_read_requests | 12 | 允许同时处于读取状态的最大请求数,决定读取并发度 |
| max_write_request_operation_count | 5120 | 单次写入跟随端的最大操作数 |
| max_outstanding_write_requests | 9 | 允许同时处于写入状态的最大请求数 |
| max_write_buffer_count | 2147483647 | 写入缓冲区最大请求数限制 |
| max_write_buffer_size | 512mb | 写入缓冲区最大内存限制 |
| max_retry_delay | 500ms | 同步失败后的最大重试延迟,会指数退避增长 |
| read_poll_timeout | 1分钟 | 当没有新数据可拉取时,长轮询等待的时间 |
这些参数不需要每次创建都调,但如果遇到同步性能瓶颈,调整空间就在这里。比如需要提高吞吐时,可以把max_outstanding_read_requests和max_outstanding_write_requests一起调大。如果带宽有限,应该限制单次读取大小和并发度,避免拉取速度过快导致网络拥塞。
2.4 弹性扩展操作:恢复、暂停、取消
CCR 机制还提供了几个日常运维经常用到的操作:
- 暂停同步:
POST /<index>/_ccr/pause - 恢复同步:
POST /<index>/_ccr/resume - 取消同步:
DELETE /<index>/_ccr/follow
暂停和恢复的典型场景是集群升级。比如跟随集群需要滚动重启时,可以先把同步暂停,等所有节点恢复后再开启同步。CCR 有 checkpoint 机制,暂停期间产生的增量日志不会丢,恢复后会自动从上次的检查点继续拉取。
取消同步的操作要谨慎,取消后跟随索引变成一个独立的普通索引,不再从领导集群拉取数据,这个操作不可逆。
3. 实操过程与核心环节实现
3.1 环境准备
为了把流程讲清楚,我这里用两个集群的测试环境作为示例:
- 领导集群:集群名
leader01,三个节点,IP 分别是 192.168.1.10 / 192.168.1.11 / 192.168.1.12 - 跟随集群:集群名
follower01,三个节点,IP 分别是 192.168.1.20 / 192.168.1.21 / 192.168.1.22
两个集群版本保持一致,TongSearch 版本选择当前稳定版本。领导集群有一个用于业务写入的索引product_orders,需要同步到跟随集群供查询使用。
先确认领导集群上索引状态正常:
GET /product_orders/_count返回结果能正常显示文档数,说明领导索引可用。
3.2 配置跟随集群的远程连接
在跟随集群的elasticsearch.yml中加上:
cluster.remote.connect: true重启跟随集群的所有节点。
然后向跟随集群发起远程集群注册:
PUT /_cluster/settings { "persistent": { "cluster.remote.leader01.seeds": ["192.168.1.10:9300", "192.168.1.11:9300", "192.168.1.12:9300"] } }建议 seeds 里写多个节点地址,这样即使其中一台节点短暂不可用,连接也能自动切换。
注册后检查连接状态:
GET /_remote/info返回结果中connected字段为true时,说明远程集群连接正常。这一步实测下来经常遇到connected: false的情况,多半是 transport 端口不通,或者集群名不匹配,需要先排查这两项。
3.3 创建跟随索引
执行创建跟随索引的请求:
PUT /product_orders_replica/_ccr/follow?wait_for_active_shards=1 { "remote_cluster": "leader01", "leader_index": "product_orders" }返回结果中acknowledged为true,且分片状态转为active,说明初始同步已经启动。
等待一段时间后,查看同步状态:
GET /product_orders_replica/_ccr/info返回信息中会包含每个分片的同步状态、检查点位置、延迟时间等关键信息。注意观察follower_index的remote_cluster和leader_index是否正确指向目标集群和索引。
3.4 验证同步结果
为了验证同步是否正常,我在领导集群上写入一条新数据:
POST /product_orders/_doc { "order_id": "20250118001", "amount": 199.00, "status": "PAID" }然后查询跟随索引:
GET /product_orders_replica/_search { "query": { "term": { "order_id": "20250118001" } } }如果查询能返回刚写入的数据,说明增量同步链路是通的。
再做一个简单的断点验证。暂停同步:
POST /product_orders_replica/_ccr/pause再往领导集群写入几条数据,然后恢复同步:
POST /product_orders_replica/_ccr/resume等待一段时间后查询跟随索引,确认暂停期间的数据也补齐了。这个验证非常值得做,有实际生产价值,我每次配置完 CCR 都会做一遍。
3.5 同步状态监控
要说真正维护 CCR,日常要盯的还是同步监控。TongSearch 提供了一套专门的 CCR 监控接口:
GET /product_orders_replica/_ccr/stats这个接口返回的字段非常多,核心关注这些:
operations_received:接收的操作总数operations_indexed:已写入跟随索引的操作总数failed_read_requests:读取失败请求数failed_write_requests:写入失败请求数read_exceptions:读取异常信息time_since_last_read:距上次读取的时间,是判断延迟的核心指标
如果time_since_last_read持续增大,说明跟随集群已经有很长时间没有从领导集群拉取数据,复制链路可能已中断。这种情况下需要检查网络、领导集群节点状态、以及远程集群连接是否正常。
另外集群维度还有一个全量查看接口:
GET /_ccr/stats可以一次看到所有跟随索引的同步状况,适合做整体巡检。
3.6 参数调优实测
默认参数下,小数据量的索引同步基本无感,但数据量一旦上来,就需要根据实际场景调整参数。
我在测试环境做过一次调优实验。领导集群有一个约 200GB 的索引,业务高峰期每分钟写入约 2 万条文档。默认参数下,跟随索引的延迟会逐渐累积,从最初的秒级延迟漂移到分钟级。
随后我调整了以下参数:
PUT /product_orders_replica/_ccr/follow?wait_for_active_shards=1 { "remote_cluster": "leader01", "leader_index": "product_orders", "max_read_request_operation_count": 10000, "max_read_request_size": "64mb", "max_outstanding_read_requests": 24, "max_write_request_operation_count": 10000, "max_outstanding_write_requests": 18, "max_write_buffer_size": "1024mb" }调整后,延迟逐渐回落并稳定在秒级。这个实验验证了一个经验:默认参数比较保守,适合小索引和低写入压力;高写入场景下,主要瓶颈通常不是网络,而是单次拉取的数据量和并发度不够,适当调高并发能明显改善同步延迟。
但也要提醒一点,调参不是无脑调大。max_outstanding_write_requests过大会导致跟随集群本地写入队列堆积,反而加重节点负担。建议调参时同步观察集群的 CPU、内存和磁盘写入延迟,找到平衡点。
4. 常见问题与排查技巧实录
4.1 复制延迟持续增大
这是 CCR 日常运维里遇到最多的问题。延迟持续增大的原因一般有三种:
- 跟随集群写入能力不足。检查跟随集群节点的 CPU、IO、磁盘水位,如果资源使用率已经很高,先扩容或削减查询压力。
- 网络带宽受限。拉取的数据量超过带宽上限,传输速度跟不上写入速度。这种情况通过监控网络流量可以确认,解决思路是限制同步并发,或者升级带宽。
- 单个分片的数据量过大,导致分片内重放耗时高。解决思路是在领导索引设计阶段就把分片数规划好,避免单个分片过大。
排查工具方面,GET /<index>/_ccr/stats返回的time_since_last_read和read_exceptions是最直接的定位入口。
4.2 创建跟随索引后一直处于初始化状态
初始化状态卡住,通常和网络或权限有关。先检查远程集群连接:
GET /_remote/info如果连接正常,再看领导集群的日志,确认是否有拉取请求到达。如果领导集群开启了安全认证,还需要检查跨集群访问的用户是否有权限读取源索引。
另一个容易被忽略的原因是源索引状态异常。如果领导索引是red状态,部分分片不可用,初始同步也会卡住。这时候优先修复领导集群的分片分配问题。
4.3 mapping 变更不同步
这是一个设计上的限制:CCR 不会自动同步领导索引的 mapping 变更,也不会同步aliases等索引元数据变更。
也就是说,如果业务在领导集群上给某个字段新增了一个子字段、修改了分词器,跟随索引那边不会自动更新 mapping,查询里用到新字段时会直接报错。
目前可行的做法是:在领导集群执行 mapping 更新后,手动在跟随索引上执行同样的 mapping 更新操作。建议把 mapping 变更流程纳入工单系统,在源端变更后同步通知跟随集群执行变更。
4.4 领导索引被删除后跟随索引不会自动删除
CCR 有一个需要重点关注的行为:当领导索引被删除时,跟随索引不会自动删除。这是因为删除操作本身不会写入 translog 的同步链路,CCR 机制感知不到源端索引的删除。
这意味着如果业务在领导集群上删了索引,跟随集群上的副本仍然存在,会持续占用磁盘空间。如果你的运维规范里有定期清理索引的流程,记得在清理领导索引之后,同步清理掉对应的跟随索引。
4.5 CCR 与备份的关系
最后提一个非常重要的原则:跨集群复制不是备份。
CCR 保证的是“数据在多个集群间的一致性”,但如果你在领导集群上执行了错误的批量删除或 update,这个错误操作会通过同步链路放大到所有跟随集群。而且跟随集群保存的是和领导集群一样的逻辑数据,不具备数据恢复功能。
生产环境一定要在 CCR 之外,另外配置快照备份机制。TongSearch 支持将索引备份到远端对象存储或共享文件系统,建议把快照备份作为最后一道防线,CCR 作为高可用和读写分离的手段,两者各司其职。
4.6 网络抖动导致的同步中断
最后分享一个实际案例。之前遇到过一台领导集群节点因为磁盘 IO 飙升,导致网络请求大量超时,跟随集群的同步任务反复失败,虽然系统会自动重试,但重试期间积累的数据较多,恢复后同步延迟飙升到十几分钟。
排查后确认是领导集群的单节点问题,故障节点恢复后同步自动追平。但整个过程中如果业务在跟随集群上做实时查询,会看到明显的延迟波动。
这类问题很难彻底避免,能做的是在架构层面加一层保护:在应用层对跟随集群的查询设置“允许的最大数据延迟”检查,当延迟超过阈值时自动切换到领导集群查询,避免业务读到明显过期的数据。
5. 后续扩展:更多复制模式
TongSearch 的跨集群复制除了单索引复制之外,还支持自动跟随模式,可以在远程集群上配置一个索引模板匹配规则,远程集群只要新建了符合条件的索引,跟随集群就会自动创建对应的跟随索引。
比如可以这样配置:
PUT /_ccr/auto_follow/leader01_auto { "remote_cluster": "leader01", "leader_index_patterns": ["logs-*", "metrics-*"], "follow_index_pattern": "{{leader_index}}-copy" }这个功能特别适合日志和监控类场景,索引按天或按小时滚动创建,手动逐个配置跟随索引不现实,自动跟随可以省掉大量重复操作。
需要留意的是,自动跟随配置本身也要纳入运维管理。每次新增的索引是否符合预期,需要定期巡检,避免某些索引被自动复制了但业务并不需要。
写在后面一点实操经验
跨集群复制这套东西,难的不是配置那几步 API,而是理解它背后的同步模型和边界条件。我在实际部署里总结了几条经验,简单写一下供参考。
第一,资源配置上,跟随集群的规格不要低于领导集群。很多人会觉得跟随集群只是“读”,配置差一点没关系,但 CCR 的同步要执行和领导集群等量的写操作,同时还要承担查询流量,资源不够的话延迟会持续累积,最后变成一个怎么调都追不上的烂摊子。
第二,监控一定要提前做,不要等出了问题再去看。把 CCR 的同步延迟、失败请求数、网络流量纳入现有监控体系,设定延迟阈值的告警,比如超过 60 秒就报警。同步沉默性故障太害人了,表层一切正常,实际上数据已经落后很久了。
第三,版本升级要谨慎。跨集群复制的两个集群版本必须保持兼容,升级时先升跟随集群,再升领导集群,避免出现跨大版本复制不兼容的情况。升级前先暂停同步,等两边集群都稳定再恢复。
跨集群复制是一个越用越觉得顺手的能力,但它的前提是稳定可靠的网络、足够的资源冗余,以及一个不依赖人工盯守的监控体系。把这三件事做好,CCR 能帮你省掉大量跨机房数据同步的烦恼;做不好,它会变成生产环境新的不稳定源。