一个分片默认不只一份——主分片(primary)之外还有副本分片(replica)。写入一条文档的时候,数据什么时候才算真正安全?是主分片写完就算,还是要等副本也写完?如果这时候正好有个副本掉线了呢?这篇讲清楚 Elasticsearch 的主副本同步机制。
这篇的核心机制用可验证的 Python 模型复现和验证,并用本机真实集群返回的字段作为直接佐证。
一条写入请求到达之后,不是"哪个分片先写完就算数",是严格按顺序走的。
写入请求先被路由到主分片(上一篇讲过路由规则),主分片本地写完,分配一个本分片内递增的序号(_seq_no)。
主分片把这次写入同步转发给所有当前在线、状态正常的副本,等它们都确认写完。
只有主分片和当前所有在线副本都写完了,客户端才会收到"写入成功"的响应——这是默认行为,不需要额外配置。
在本机真实运行的 Elasticsearch 上写两条数据,看返回结果。
每次写入的响应里都带着 _seq_no(这个分片内部的写入序号,单调递增)和 _primary_term(当前主分片的"任期号")。_primary_term 现在是 3,是因为这台机器在准备这个系列的过程中,这个分片对应的主分片经历过几次真实的重启/重新选主——每一次,这个数字都会真实地往上跳一格,不会退回去。
用一个可验证的最小模型(1 主 + 2 副本)复现完整流程。
verified simulation 这个演示的场景是:第一条写入一切正常;第二条写入时 replica-B 恰好掉线——不阻塞写入,但被移出"in-sync 集合";第三条写入延续这个状态;接着模拟主分片彻底失效,replica-A(还在 in-sync 集合里)被提升为新主分片,primary_term 从 1 跳到 2;最后验证一次带着"旧任期号"的写入会被拒绝,带着"新任期号"的会被接受——这是防止网络分区导致的旧主分片继续对外提供服务("裂脑")的核心机制。
被移出 in-sync 集合的副本,不代表被永久放弃。
这套设计的核心权衡是:宁可让"确认过的写入"数量变少(容忍副本暂时掉线,不因为一个副本卡住就让所有写入超时失败),也不让"确认过的写入"变得不可靠(掉线的副本绝不会被当成"已经确认"的一员)。