4. Elasticsearch跨集群迁移(CCR):跨集群复制原理、配置CCR、故障切换与恢复

跨集群复制,简称CCR。说白了,就是让两个Elasticsearch集群之间,能实时同步数据。

我最早接触CCR,是在一次数据中心迁移的项目里。老集群在北京,新集群在上海。业务要求零停机,数据不能丢。嗯,那时候我就开始研究CCR了。

4.1 CCR的核心原理

CCR基于Elasticsearch的soft.deletes机制。你可能会问,什么是soft deletes?

简单说,就是文档被删除或更新时,旧版本不会立即物理删除。而是标记为“已删除”,保留一段时间。这个保留期,由index.soft_deletes.retention_lease.period控制,默认12小时。

CCR的工作流程,其实就三步:

  1. 初始同步:从集群(Follower)先做一次全量恢复,把主集群(Leader)的索引数据完整拉过来。
  2. 实时跟踪:Follower持续读取Leader的translog(事务日志),获取增量变更。
  3. 重放操作:Follower把读取到的变更,在自己的索引上重放一遍。

这里有个关键点——CCR是拉模式。Follower主动去Leader拉数据,而不是Leader推给Follower。这样做的好处是,Follower可以控制节奏,不会把集群压垮。

核心概念速查表

角色说明我见过的坑
Leader索引数据源,可写别在Leader上做force merge,会破坏soft deletes
Follower索引副本,只读有人试图直接写Follower,报错后一脸懵
CCR任务复制管道一个Follower只能跟一个Leader,不能多对一

4.2 配置CCR的实战步骤

配置CCR之前,有几个前提条件你得先确认:

  • 两个集群版本必须一致。我见过有人用7.10和7.17配CCR,结果各种诡异报错。
  • Leader集群必须开启soft deletes。7.x之后默认开启,但如果你是从6.x升上来的,记得检查一下。
  • 网络要通。Follower能访问Leader的9300端口(transport层)。

4.2.1 在Leader集群上注册Follower

先在Leader集群上,把Follower集群的信息注册进去:

PUT /_cluster/settings
{
  "persistent": {
    "cluster.remote.follower_cluster.seeds": ["192.168.1.101:9300", "192.168.1.102:9300"]
  }
}

我个人习惯把集群别名起得有意义一点。比如dc_shanghaidc_beijing,别用cluster1cluster2这种。时间久了你自己都分不清。

4.2.2 在Follower集群上创建CCR任务

有两种方式:

方式一:手动创建Follower索引

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

方式二:自动跟随模式

如果你有一堆索引要复制,一个个手动创建太累了。可以用自动跟随:

PUT /_ccr/auto_follow/my_auto_pattern
{
  "remote_cluster": "leader_cluster",
  "leader_index_patterns": ["logs-*", "metrics-*"],
  "follow_index_pattern": "{{leader_index}}-copy"
}

我曾经在一个项目里,用自动跟随模式一次性复制了200多个索引。省了不少事。但要注意,自动跟随只对新创建的索引生效。已有的索引,你还是得手动处理。

4.2.3 监控CCR状态

配置完之后,怎么知道它跑得好不好?

GET /my_index/_ccr/info

重点关注这几个字段:

  • leader_index_operations:Leader上总操作数
  • follower_index_operations:Follower上已复制的操作数
  • follower_time_since_last_read:上次读取Leader的时间。如果这个值持续增长,说明复制卡住了。
  • read_exceptions:读取异常次数。不为0就要查原因了。

我的小技巧:写一个定时脚本,每小时检查一次follower_time_since_last_read。如果超过5分钟没更新,就发告警。这样能第一时间发现复制延迟。

4.3 故障切换与恢复

CCR最让人头疼的,就是故障切换。说白了,就是Leader挂了,怎么让Follower顶上。

4.3.1 手动故障切换

当Leader集群彻底不可用时,你需要手动把Follower提升为新的Leader:

  1. 暂停CCR任务POST /my_index/_ccr/pause_follow
  2. 关闭只读PUT /my_index/_settings { "index.blocks.write": false }
  3. 更新应用配置:把写入地址从Leader改成Follower

嗯,这里有个坑。我曾经在切换时忘了暂停CCR任务,结果Leader恢复后,Follower还在尝试连接,导致数据冲突。所以顺序很重要——先暂停,再切写。

4.3.2 故障恢复后的数据回迁

Leader恢复后,你想把数据迁回去。怎么做?

我的建议是:

  1. 在原来的Leader上,创建一个新的CCR任务,从Follower复制数据回来。
  2. 等数据追平后,再切一次写入。
  3. 删除旧的CCR任务。

说白了,就是角色互换。原来的Leader变成Follower,原来的Follower变成Leader。等数据同步完了,再换回来。

警告:千万不要在故障切换后,直接删除Leader索引重建CCR。那样会丢失未同步的数据。正确的做法是,保留Leader索引,等数据追平后再做切换。

4.3.3 自动故障切换(收费功能)

如果你用的是Elasticsearch白金版,可以配置自动跟随和自动切换。但说实话,我建议你还是手动操作更稳妥。自动切换虽然方便,但一旦出问题,排查起来很麻烦。

你想想看,自动切换的逻辑是:检测到Leader不可用 → 自动提升Follower → 通知应用切换写入。这个链条上任何一个环节出问题,都可能导致数据不一致。而手动操作,至少每一步你都知道发生了什么。

4.4 避坑指南

最后,分享几个我踩过的坑:

  • 版本不一致:CCR要求主从集群版本完全一致。小版本不同也不行。我吃过7.10.0和7.10.1的亏。
  • 磁盘空间:Follower需要跟Leader一样多的磁盘空间。别以为Follower只存增量数据,初始同步是全量拷贝。
  • 网络延迟:跨机房复制时,网络延迟会导致复制延迟。如果延迟超过soft deletes保留期(默认12小时),复制会失败。建议跨机房场景下,把保留期改成24小时。
  • Mapping变更:Leader索引的mapping变了,Follower不会自动同步。你得手动更新Follower的mapping,否则新字段的数据会丢失。

CCR是个好东西,但也不是银弹。它适合做灾备、做读写分离、做数据中心迁移。但如果你需要双向同步,或者需要跨版本迁移,那CCR就不太合适了。选工具之前,先想清楚自己的场景。