分片与副本机制#
一句话答案#
ES 通过 Primary Shard 水平拆分数据(创建后不可修改数量),通过 Replica Shard 提供高可用和读扩展;路由公式 shard = hash(_routing) % number_of_shards,单个 Shard 建议 10-50GB。
核心要点
分片模型#
Index: products (5 Primary + 1 Replica = 10 Shards)
Node1: [P0] [R2] [R4]
Node2: [P1] [R3] [P4]
Node3: [P2] [R0] [R1] ← Primary 和 Replica 分布在不同节点
规则:Primary 和 Replica 不在同一节点(保证节点故障不丢数据)plaintext路由机制#
shard_num = hash(_routing) % number_of_primary_shards
默认 _routing = _id(文档ID)
自定义路由:按 user_id 路由,同一用户数据在同一分片 → 避免跨分片查询
PUT /orders/_doc/123?routing=user_456plaintext为什么分片数创建后不能修改?#
因为路由公式依赖 number_of_primary_shards:
hash("doc_123") % 5 = 2 (5 分片时在 shard 2)
hash("doc_123") % 7 = 4 (改成 7 分片后找不到了)
解决方案:需要增加分片时 → reindex 到新索引
或提前创建足够多分片(但不能太多)plaintext分片数规划#
| 因素 | 建议 |
|---|---|
| 单 Shard 大小 | 10-50GB(过大影响恢复速度) |
| 每节点 Shard 数 | ≤ 20 个/GB heap(避免内存压力) |
| 集群总 Shard 数 | 几千个还行,上万需注意 Master 负载 |
| 初始规划 | 预估数据量 / 30GB = 分片数 |
例:预计 300GB 数据
分片数 = 300GB / 30GB = 10 个 Primary Shard
副本数 = 1(生产必备)
总 Shard = 10 × 2 = 20
3 节点集群 → 每节点约 7 个 Shardplaintext副本的作用#
- 高可用:Primary 所在节点挂了,Replica 自动提升为 Primary
- 读扩展:查询请求可以分摊到 Replica(读多写少时有效)
- 数据安全:多副本防止数据丢失
副本同步流程#
写入流程:
1. 请求路由到 Primary Shard
2. Primary 写入成功
3. Primary 并行转发到所有 Replica
4. 所有 Replica 确认 → 返回客户端成功
配置:
wait_for_active_shards: "all" (强一致) / 1 (仅 Primary) / "majority"plaintext副本一致性模型(深挖)#
ES 用的是 primary-backup 复制,不是 quorum 写。 写入只认 Primary:Primary 先在本地写成功,再把操作并行转发给当前 in-sync 集合里的所有 Replica,等它们全部确认后才返回客户端成功。这里没有”写多数派就算成功”的概念——in-sync 的副本必须全部确认。
in-sync 副本机制(类似 Kafka 的 ISR):
in-sync 集合 = 当前与 Primary 数据同步、可参与确认的副本集合
某个 Replica 复制失败 / 响应超时:
1. Primary 不会一直死等,也不会因为它失败就让整个写入失败
2. Primary 上报 Master:把这个慢/挂的 Replica 标记为 stale
3. Master 将该 Replica 踢出 in-sync 集合(从 allocation 中移除)
4. 后续写入只需剩下的 in-sync 副本确认 → 保证可用性
5. 该副本后续重新追上数据后,再被加回 in-sync 集合plaintext这样既保证”返回成功的写一定落在所有 in-sync 副本上”(一致性),又不会被一个掉队副本拖垮(可用性)。
local checkpoint / global checkpoint(同步进度追踪):
| 概念 | 含义 |
|---|---|
| seq_no | 每条写操作在分片上的单调递增序列号 |
| local checkpoint | 单个分片(Primary 或某 Replica)上”该序号及之前的操作都已落盘”的位点 |
| global checkpoint | 所有 in-sync 副本的 local checkpoint 取最小值——代表”全集群都已安全持有”的位点 |
global checkpoint 的作用:① 主从切换 / 副本恢复时,落后的副本只需从 global checkpoint 之后做增量补齐(基于 seq_no 的 ops-based recovery),不必整段拷贝;② 标记数据安全水位。
与 Raft 的本质区别:
- ES 数据面(文档读写)= 主备复制:Primary 单点决定写顺序,Replica 被动跟随,靠 in-sync + checkpoint 保证不丢,不跑共识算法。
- ES 集群面(元数据/选主)= 类 Raft 的共识协议(7.x 起内置基于 Raft 思想的协调层,保证集群状态唯一)。
- 所以”ES 写入要多数派 / ES 用 Raft 写数据”是典型错误:选 Master 才用共识,写文档走的是 primary-backup。
分片分配策略#
ES 自动均衡分片:
- 新建索引时均匀分配到各节点
- 节点加入/离开时自动 rebalance
- 可通过 allocation awareness 指定机架/区域感知
手动控制:
PUT /_cluster/settings
{
"transient": {
"cluster.routing.allocation.exclude._name": "node_hot_1"
}
}plaintext面试回答(2分钟版)
ES 分片分 Primary 和 Replica。Primary Shard 决定数据怎么拆分,路由公式是 hash(routing) % primary_shard_num,所以分片数创建后不能改(改了路由就乱了)。单个分片建议 10-50GB,太大恢复慢,太小浪费资源。Replica 提供高可用——Primary 所在节点挂了 Replica 自动提升,同时还能分摊读请求。写入时先写 Primary,再并行同步到所有 Replica,全部确认才返回成功。分片规划的关键是预估数据增长量:300GB 数据大约需要 10 个分片。Primary 和 Replica 必须在不同节点,ES 自动做分片均衡和故障恢复。需要扩容分片数时只能 reindex 到新索引,所以初期规划要留余量。
追问与易错
追问方向:
- “怎么扩容?”→ 增加节点 ES 自动 rebalance;增加分片数需要 reindex
- “自定义路由有什么好处?”→ 同一维度数据在同一分片,避免跨分片查询(如按租户路由)
- “副本越多越好吗?”→ 不是,副本多写入变慢(每次写入要同步),且占磁盘
- “节点挂了多久恢复?”→ 默认 1 分钟探测超时后开始分配 Replica 为 Primary + 复制新副本
- “ES 写入是 quorum/多数派吗?”→ 不是,是 primary-backup + in-sync 副本:要 in-sync 集合里的副本全部确认;副本失败则被 Master 标记 stale 踢出 in-sync,不阻塞写入
- “ES 用 Raft 吗?”→ 只有选主/集群元数据用类 Raft 共识;文档读写是主备复制,靠 local/global checkpoint 追踪进度,不跑共识
易错点:
- ❌ “分片数可以动态调整”——Primary 数创建后不可变,只能 reindex
- ❌ “副本数也不能改”——副本数可以动态调整(PUT index/_settings)
- ❌ “分片越多查询越快”——每个分片有查询开销,过多分片反而慢(协调节点合并压力)