土法炼钢兴趣小组的算法知识备份

【全文检索引擎】Index · Shard · Replica:路由与 cluster state

文章导航

分类入口
databasedistributed
标签入口
#elasticsearch#lucene#shard#replica#routing#cluster-state#primary-shard#scatter-gather#8.x

目录

第 7 篇Lucene Index 讲成「段 + IndexWriter + NRT Reader」的单机模型;第 10 篇 说明排序与聚合走 DocValues 列式侧面。到了 Elasticsearch,运维界面里出现的 Index 并不是 Lucene Index 的同义词复刻——它是 逻辑命名空间 + 分片路由表 + 副本策略 的分布式封装。第一次给索引设 number_of_shards: 50 时若只想到「并行度」,却忽略 每个主分片都是独立 Lucene 索引、都会在 cluster state 里占一条路由元数据,几个月后 master 的 Cluster State Update(CSU)变慢,往往与分片数而非文档条数更相关。

本文锚定 Elasticsearch 8.x ReferenceIndex modulesClusterShard allocation),回答四个会直接决定容量规划的问题:

本文是「全文检索引擎」系列第 11 篇(共 18 篇)。→ 系列目录

版本锚定:Elasticsearch 8.x Reference;底层 Lucene 9.x(与 ES 8.15 发行版一致)。本篇不写千节点吞吐自测;机制结论来自官方文档与 Lucene 段不可变模型。


一、三层名词:ES Index、Shard、Lucene Segment

层级 对象 可变性 本篇关注点
集群 Index(逻辑) 映射、设置、别名可变 路由表、副本因子
节点 Shard(物理 Lucene Index) 段内不可变;通过 refresh/merge 演进 主/副本、分配
Segment 写入后不可变 第 7–8 篇

一个 Primary Shard 在数据节点上就是一个完整的 Lucene 索引目录:拥有自己的 FST 词典、postings、DocValues 文件集合(第 4–5、10 篇)。Replica 不是「只读缓存文件」,而是 与主分片同构的 Lucene 索引副本,通过复制索引操作保持段集合一致(复制细节与 translog 在 第 12 篇)。

flowchart TB
  IDX["ES Index<br/>settings + mapping + alias"]
  P0["Primary shard 0<br/>Lucene Index on node A"]
  P1["Primary shard 1<br/>Lucene Index on node B"]
  R0["Replica shard 0<br/>Lucene Index on node C"]
  R1["Replica shard 1<br/>Lucene Index on node D"]
  IDX --> P0
  IDX --> P1
  P0 -. replication .-> R0
  P1 -. replication .-> R1
  P0 --> S0["Segments"]
  P1 --> S1["Segments"]

创建索引时 number_of_shards(主分片数)在 ES 8.x 中 之后不可改(要改变分片数需 reindex 或 shrink/split 等迁移手段,见官方 Index modules)。number_of_replicas 可随时调整——它只改变 每个主分片挂多少副本,不改变路由空间大小。


二、主分片路由:哈希取模与 scatter-gather

2.1 默认路由公式

对单条文档的索引、GET、DELETE,协调节点(或接收写入的数据节点)先算 目标主分片

\[ \text{shard\_id} = \text{hash}(\text{routing}) \bmod N_{\text{primary}} \]

因此 同一路由键的文档共址 在同一主分片——这与 Redis Cluster 的 hash tag 共槽动机类似(见 Redis Cluster),但 ES 没有 跨分片多文档原子事务;共址只为 定向查询关联文档局部性

PUT /orders/_doc/1001?routing=user_42
{
  "user_id": "user_42",
  "amount": 128
}

查询时带上相同 routing,协调节点可把请求 扇入单个主分片(及其被选中的副本),避免全索引 scatter-gather。若省略 routing,按 _id 哈希,则 _id 点查 仍只需一片,但 无法 保证「同一 user_id 的全部订单」落在一片——除非写入时统一用 user_id 作 routing。

2.2 搜索为何常要查所有分片

匹配查询(如 matchquery_string)在不知道词项落在哪些文档的前提下,倒排相交必须在 每个主分片 上各做一遍,再在协调节点 归并第 13 篇)。这就是分布式倒排的经典 scatter-gather:与 Zobel & Moffat 综述里「分区倒排 + 结果合并」模型一致——并行度来自分片数,协调开销来自 跨分片归并 Top-K全局统计(可选 DFS,见第 13 篇)。

访问模式 是否必须全分片 典型 API
_id / 已知 routing GET /idx/_doc/id?routing=...
全文 / 过滤 / 聚合 是(除非 routing 裁剪) _search_msearch
按时间滚动索引 可用别名裁剪索引集 index template + ILM

自定义 routing 的风险搜索引擎架构 第三节已用「大卖家订单倾斜」说明:哈希均匀 ≠ 业务均匀。本篇只强调机制——热点主分片 无法靠加副本根治,因为 写入仍打在主分片


三、副本:读扩展、容错与分配约束

每个主分片可有 index.number_of_replicas 个副本(默认 1)。副本分片 不参与写入路由;写入路径总是 主分片 → 复制到副本(复制与 refresh 顺序见第 12 篇)。

读路径上,协调节点对 每个主分片副本组 挑选一个 活跃分片 执行查询——默认在组内 轮询 以均衡负载(Reference SearchPreference 可覆盖)。因此增加副本可线性扩展 只读 吞吐,但 不改变 单文档写入的主分片瓶颈。

sequenceDiagram
  participant C as Coordinating node
  participant P as Primary shard
  participant R as Replica shard
  C->>P: index / bulk (write)
  P-->>R: replicate indexing ops
  C->>R: search phase (read may hit replica)
  R-->>C: local top hits + aggs

分片分配器(Shard Allocator)在 master 上运行,约束包括(Reference ClusterShard allocation):

  1. 主分片与副本 不同节点(否则节点宕机同时丢主备)。
  2. 磁盘水位线:超过 cluster.routing.allocation.disk.watermark.high 不再迁入新分片。
  3. 可选 awareness attributes(机架/可用区)强制主备跨域。

architecture/42 对照:该文用运维故事说明 分片数膨胀 → CSU 变慢;本篇钉机制——副本数翻倍 = 活跃分片数近似翻倍,同样放大 cluster state 与分配心跳,而非「免费高可用」。


四、cluster state:元数据体积与更新代价

Cluster state 是 master-eligible 节点维护的 集群级一致快照,包含但不限于(Reference ClusterCluster state):

任何创建索引、改 mapping、分片开始/完成恢复、节点上下线,都会触发 新的 cluster state 版本 并广播给所有节点。数据节点收到后更新本地路由表,搜索与写入才能找到正确分片。

4.1 为何分片数是「元数据维度」

每个 Lucene 分片在 JVM 堆内持有段元数据、缓存词典句柄、文件描述符;在 cluster state 里则持有 路由条目 + 恢复状态机。分片数 \(S\) 增大时:

官方 sizing 指导(Reference Size your shards,B 级工程建议)强调:单分片目标体积(常见建议 10–50 GB 量级,随硬件与查询类型变化)与 每 GB 堆内存可分片数上限——目的是让 \(S\) 与数据量匹配,而不是「分片越小越安全」。

4.2 运维可观测:从指标反推 \(S\)

不写集群 QPS,只列 与分片数直接相关 的只读诊断(ES 8.x _cat API):

# 活跃分片总数(primary + replica)
curl -s 'http://localhost:9200/_cat/shards?v&h=index,shard,prirep,state,node' | head

# 单节点承载分片数是否倾斜
curl -s 'http://localhost:9200/_cat/allocation?v'

# 集群状态体量间接信号:pending tasks 堆积
curl -s 'http://localhost:9200/_cluster/pending_tasks?pretty'

pending cluster state tasks 长期非空且与 批量建索引 / 分片恢复 同期出现,应优先怀疑 \(S\) 或索引数 \(I\) 过大,而不是先加数据节点 CPU。完整症状树见 第 17 篇

4.3 与 Lucene 单索引多段的对比

第 8 篇 讨论 段过多 拖慢查询,解法是 merge。分片过多 是分布式层的同类问题,但 不能 用 Lucene merge 合并——只能 shrink、reindex、合并索引模板,或从设计上减少 \(I \times N_{\text{primary}}\)。这是 工程间隙:论文里的分布式倒排分区常假设分片数固定且远小于生产误配下的指数增长。


五、学术谱系、争论与开放问题

谱系:倒排索引分区 + 查询广播(Zobel & Moffat, Inverted Files for Text Search, 2006 survey)→ 各系统固定分区数(ES 创建时锁定 number_of_shards)→ 协调层 scatter-gather + 可选全局统计(DFS)→ 云原生日志栈按 时间索引 控制 \(I\)(与 observability/08 日志选型 衔接)。

争论

  1. 固定分片数 vs 动态重分片:ES 选择创建时固定以降低运行时迁移复杂度;Cassandra/Dynamo 风格动态环在 成员变更 上更灵活,但倒排 段复制成本 远高于键值行迁移。二者优化目标不同,不宜用「环更优雅」否定固定分区。
  2. 副本读扩展 vs 写路径主分片:增加副本对 写入延迟 可能略增(复制 fan-out),对 热点写 无帮助——这与 architecture/42 强调的规划失误(分片过碎)是同一运维面。

开放问题


六、常见误解

误解 事实
「ES 的 index = Lucene 的 index」 ES index 是 逻辑层;每个 primary shard 才对应一个 Lucene index
「副本越多写入越快」 写入只走 主分片;副本增加复制开销,只扩展
number_of_shards 可以随时改大」 主分片数创建后 不可改;只能迁移数据或建新索引
「哈希路由保证负载均衡」 保证的是 键空间均匀;业务热点(大 routing)仍会造成 单分片热点
「分片越小越安全」 过碎分片使 \(S\) 暴涨,cluster state 与段固定开销 主导故障模式(见 architecture/42)
「协调节点不参与计算」 协调节点做 路由、归并、fetch 二次扇出;高扇出时它也会成为尾延迟点

七、小结

三句话小结

  1. Elasticsearch 把 每个主分片 实现为节点上的 独立 Lucene 索引hash(routing) mod N 决定写入落点,默认 routing 为 _id
  2. 副本 分担读负载与容错,但不改变写路径;协调节点在每个副本组内挑选执行分片,分配器强制主备异节点。
  3. cluster state 随分片数与索引数增长而膨胀,CSU 与广播成本是分布式层的硬约束;分片规划应对标数据体积与查询扇出,而非盲目追求「更多并行」。

下一篇拆解 refresh、flush、translog 三者的保证边界,以及 refresh_intervalwait_for 如何塑造近实时语义:refresh · flush · translog


参考资料

规范与文档(A 级)

  1. Elasticsearch 8.x Reference, Index modulesIndex settingsnumber_of_shards / number_of_replicas
  2. Elasticsearch 8.x Reference, Document APIsRouting
  3. Elasticsearch 8.x Reference, ClusterCluster stateShard allocation
  4. Elasticsearch 8.x Reference, Size your shards(分片体积与堆内存指导)。
  5. Apache Lucene 9.x, IndexWriter / Segment 不可变模型(与 第 7 篇 一致)。

论文与综述(A 级)

  1. Zobel, J. & Moffat, A., Inverted Files for Text Search, 2006(分布式倒排分区与合并语义)。

站内对读(B 级)

  1. 搜索引擎架构(分片策略与膨胀 症状,本篇不复述全文)。
  2. 本系列 第 7 篇第 10 篇第 12 篇

返回 系列目录 | 上一篇:DocValues 与 stored fields | 下一篇:refresh · flush · translog

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-06-18 · database / distributed

【列存引擎内核】ReplicatedMergeTree

ReplicatedMergeTree 副本协调:Log entry、ClickHouse Keeper/ZooKeeper 路径、副本同步与 recovery。双节点实验步骤(本环境未部署)。24.x LTS 默认推荐 Keeper。


By .