深入 Elasticsearch(11):故障恢复与读写模型
上一篇解决了数据怎样分布到 shard 上——路由公式和分片数不可变的约束。这一篇进入 shard 的副本机制和故障恢复。
每个 primary shard 可以有零个或多个 replica shard。Replica 提供两个能力:高可用(primary 故障时 replica 接管)和读扩展(搜索请求可以命中 replica)。理解 primary-replica 的同步模型,才能在一致性和可用性之间做出正确的配置选择。
本文只抓一个问题:primary 和 replica 之间怎样同步数据,primary 故障时怎样恢复。
写入模型
一条文档的写入路径经过 primary shard 再转发到 replica:
1 | |
这个过程涉及 4 个角色的交互,时序上有严格的先后依赖:
sequenceDiagram
participant C as Client
participant CN as Coordinating Node
participant P as Primary Shard
participant R as Replica Shard(s)
C->>CN: 写入请求
CN->>P: 路由到 primary
P->>P: 写入 buffer + translog
P->>R: 并行转发写入
R->>R: 本地执行写入
R->>P: 返回确认
P->>CN: 所有 in-sync replica 已确认
CN->>C: 返回成功
关键点在第 4-6 步:primary 写入成功后并行转发给所有 in-sync replica,等全部确认后才向上返回。任何一个 in-sync replica 超时未响应,master 会将它从同步集合中移除,而不是无限等待。
这是同步复制模型——primary 等待所有 in-sync replica 确认后才返回成功。wait_for_active_shards 参数可以控制需要多少个活跃 shard 确认才算写入成功:
| 值 | 含义 |
|---|---|
1(默认) |
只要 primary 写入成功就返回(replica 异步) |
all |
所有 shard(primary + 所有 replica)都确认 |
N(数字) |
至少 N 个 shard 确认 |
注意默认值 1 意味着只等 primary 确认。Replica 的写入是在 primary 返回成功之后并行进行的——如果 primary 在转发到 replica 之前崩溃,这条数据可能丢失。设 wait_for_active_shards: all 可以保证所有 replica 都写入成功,但延迟更高。
In-Sync Allocation IDs
ES 维护一个 in-sync allocation IDs 集合,记录哪些 shard 副本和 primary 保持同步。这个概念类似 Kafka 的 ISR(In-Sync Replicas)。
当一个 replica 落后太多(比如网络分区、节点重启),master 会把它从 in-sync 集合中移除。被移除的 replica 不再接收写入请求,需要做 recovery 才能重新加入。
读取模型
搜索请求可以命中 primary 或任何 in-sync replica。ES 使用 adaptive replica selection(ARS)自动选择延迟最低的副本:
1 | |
ARS 综合考虑节点的搜索队列长度、历史响应时间、节点负载来做选择。这意味着搜索负载自动在 primary 和 replica 之间负载均衡。
Replica 数量越多,搜索吞吐越高(更多副本可以服务搜索请求),但写入吞吐越低(每次写入需要同步到更多副本)。
故障恢复
Primary 故障
当 primary shard 所在节点故障时:
1 | |
从 primary 故障到恢复完成,整个集群经历一次状态迁移:
stateDiagram-v2
[*] --> Normal: 集群正常运行
Normal --> Detected: primary 所在节点故障
Detected --> Promoting: master 选择 in-sync replica
Promoting --> NewPrimary: replica 提升为 primary
NewPrimary --> Serving: 新 primary 接收读写
Serving --> Recovery: 故障节点恢复上线
Recovery --> Syncing: 旧 primary 作为 replica 追赶数据
Syncing --> Normal: recovery 完成,重新加入 in-sync 集合
注意 Promoting 到 NewPrimary 这一步——master 只从 in-sync 集合中选择候选者,落后太多的 replica 不会被提升,避免数据回退。故障节点恢复后角色反转:原来的 primary 变成 replica,通过 peer recovery 追赶新 primary 的数据。
Shard Recovery
当一个 shard 需要恢复(节点重启、replica 重新加入)时,ES 执行 peer recovery——从 primary 复制数据到需要恢复的 replica:
1 | |
Recovery 期间 primary 继续正常服务。Recovery 完成后,恢复的 shard 加入 in-sync 集合,开始接收新的写入和搜索请求。
Unassigned Shards
当 shard 无法被分配到任何节点时,状态变为 UNASSIGNED。常见原因:
- 节点数不足(replica 不能和 primary 在同一节点)
- 磁盘水位线超标
- allocation filtering 排除了所有可用节点
用 _cluster/allocation/explain 定位原因:
1 | |
实验:观察副本行为
1 | |
模式提炼:主从复制 + 同步确认
1 | |
| 维度 | 同步复制 | 异步复制 |
|---|---|---|
| 持久性 | 高(所有副本确认) | 低(可能丢失最近写入) |
| 写入延迟 | 高(等待最慢的副本) | 低 |
| 适用场景 | 金融数据、不可丢失 | 日志、可重放 |
| ES 配置 | wait_for_active_shards: all |
wait_for_active_shards: 1(默认) |
工程迁移表
| 概念 | Elasticsearch | Kafka | MySQL | MongoDB |
|---|---|---|---|---|
| 复制单元 | Shard (primary/replica) | Partition (leader/follower) | Database (master/slave) | ReplicaSet (primary/secondary) |
| 同步机制 | 转发写入操作 | Log replication | Binlog replication | Oplog replication |
| 同步集合 | In-sync allocation IDs | ISR (In-Sync Replicas) | Semi-sync replicas | Majority write concern |
| 读取路由 | Adaptive replica selection | Consumer fetches from leader/follower | 读写分离中间件 | Read preference |
| 故障切换 | Master 提升 replica | Controller 选新 leader | MHA / orchestrator | 自动选举 |
常见误解
误解一:replica 越多数据越安全。 Replica 数量增加提升了读吞吐和容错能力,但每个 replica 都需要存储完整的 shard 数据。3 个 replica 意味着 4 倍存储。且写入延迟随 replica 数增加。通常 1-2 个 replica 就足够。
误解二:replica 和 primary 的数据完全实时一致。 默认 wait_for_active_shards: 1 时,写入返回后 replica 可能还没完成同步。极短时间窗口内搜索 replica 可能看不到最新写入。
误解三:单节点集群设置 replica 有意义。 单节点集群中 replica 无法分配(不能和 primary 在同一节点),集群状态始终是 yellow。单节点应该设 number_of_replicas: 0。
练习
-
在单节点集群中创建一个 1 replica 的 index,用
_cluster/allocation/explain查看 replica 无法分配的原因。然后设 replica 为 0,观察集群状态变为 green。 -
用
_cat/shards?v查看 shard 分布,理解prirep(p=primary, r=replica)、state、node字段的含义。 -
查看
_cat/recovery了解正在进行或最近完成的 recovery 操作。
系列导航
| 上一篇 | 下一篇 |
|---|---|
| 分片与路由:数据分布的核心机制 | 集群协调:Master 选举与集群状态同步 |
