分片大小的黄金法则与再平衡策略

22 May 2026 – wusfe · 5 min read

分片大小的黄金法则与再平衡策略

分片(shard)是 ES 中最底层的 Lucene 索引实例,也是数据分布的最小单元。分片数量和大小设计不当,轻则查询变慢,重则集群不可用。本文介绍分片规划的黄金法则和实操策略。

单分片最佳大小:10-50GB

业界公认的单分片最佳大小是 10GB 到 50GB。这个数字背后有详实的工程依据:

为什么不能太小(< 10GB)?

分片过小意味着分片数量过多。想象一个 50GB 的索引切成了 50 个 1GB 的分片,每个节点要维护几十个分片的小索引实例:

  • 每个分片都是一个完整的 Lucene 索引,维护自己的 segment 文件、commit point、translog。
  • 分片越多,搜索请求需要在更多分片上执行,协调节点的 merge-sort 开销线性增长。
  • 分片元数据存储在 Cluster State 中,分片数过多会撑大 Cluster State,导致 Master 节点 GC 压力增大。

为什么不能太大(> 50GB)?

  • 迁移慢:节点宕机后,分片需要从剩余副本重建。50GB 在千兆网络下全量拷贝约需 7 分钟,100GB 要 14 分钟。迁移期集群处于 degraded 状态。
  • 恢复慢:分片 Recovery 不光传输文件,还要回放 translog。分片越大,translog 越大,回放越久。
  • Merge 慢:Lucene 的后台 Merge 操作需要重写 segment 文件。一个 100GB 分片的 Major Merge 可能持续数小时,期间磁盘 IO 几乎被 Merge 独占,严重挤占写入和查询资源。

具体规划公式

单分片大小 = 总数据量 / 主分片数 / (1 + 副本数)

通常按照 30-40GB 来定。比如一个预计 3TB 的订单索引,可以设:

主分片数 = 3000GB / 30GB ≈ 100 → 主分片 10 个 + 2 副本 = 30 个分片总数 → 每分片约 100GB × 3 / 30 = 10GB × ... 

更简洁的公式:主分片数 = ceil(总数据量 / 30GB),再根据节点数适当调整。

分片过多 vs 分片过大:两难对比

问题 分片过多(单分片 < 10GB) 分片过大(单分片 > 50GB)
搜索延迟 协调节点归并开销大,扇出多 单分片内 segment 多,遍历慢
写入吞吐 无明显影响 可能因单分片 merge 影响
节点恢复 分片多并行恢复快但总开销大 单个分片恢复慢,串行瓶颈
Master 压力 Cluster State 大,发布慢
磁盘浪费 可能有多余的小 segment 紧凑

分片过多的踩坑案例:某日志平台的默认索引模板设置了 50 个主分片(因为最初规划的数据量很大),但实际数据量只有 50GB/天。结果每天每个索引只有 1GB 的分片,半年下来集群 shard 总数接近 10000。Master 节点的 Cluster State 发布一次要 3 秒,节点加入集群要 2 分钟。

分片过大的踩坑案例:某电商将订单索引设计为 2 主分片 + 1 副本,一个月后单分片达到 120GB。一次节点滚动重启,分片恢复耗时 40 分钟,所有查询超时。最终不得不停机 reindex 到 6 个分片。

再平衡策略

自动再平衡

ES 默认开启自动分片再平衡,受以下参数控制:

PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.rebalance.enable": "all",
    "cluster.routing.allocation.allow_rebalance": "indices_all_active",
    "cluster.routing.allocation.cluster_concurrent_rebalance": 2,
    "indices.recovery.max_bytes_per_sec": "40mb"
  }
}
  • cluster_concurrent_rebalance:集群级并发迁移分片数,默认 2。太小了慢,太大了影响业务。
  • indices.recovery.max_bytes_per_sec:每个节点的恢复带宽限制,默认无限制。建议设为 40mb 防止打满网卡。

手动词 reroute 迁移

当自动平衡不满足需求时(比如想把热点索引从该节点移走),可以用 reroute API:

POST /_cluster/reroute
{
  "commands": [
    {
      "move": {
        "index": "hot_index_2024",
        "shard": 0,
        "from_node": "node-1",
        "to_node": "node-3"
      }
    }
  ]
}

reroute 三大命令

命令 作用 风险
move 将已分配的分片从节点 A 移到节点 B 无风险,正常迁移
allocate_replica 从未分配的副本分配指定副本 无风险
allocate_empty_primary 分配空主分片(强制) 数据将丢失!仅用于灾难恢复
cancel 取消正在进行的 allocation 可用于终止异常 recovery

total_shards_per_node 限制

PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.total_shards_per_node": 600
  }
}

这个参数限制每个节点能承载的主分片+副本总数。当某节点超过限制时,新索引的分片不会分配到该节点。强烈建议生产环境设置此参数,防止某个节点分片数爆炸。

更多节点级限制(ES 7.16+):

PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.total_primary_shards_per_node": 300,
    "cluster.routing.allocation.total_shards_per_node": 600
  }
}

可以分别限制主分片数和总分片数,更精准。

磁盘水位线保护

PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": "85%",
    "cluster.routing.allocation.disk.watermark.high": "90%",
    "cluster.routing.allocation.disk.watermark.flood_stage": "95%",
    "cluster.info.update.interval": "30s"
  }
}
  • low (85%):不再向该节点分配新分片(现有分片不受影响)
  • high (90%):开始将分片从该节点迁出
  • flood_stage (95%):将所有包含该节点分片的索引设为 read_only_allow_delete

分片分配感知(Allocation Awareness)

额外提一下 shard allocation awareness,可以防止所有副本落在同一个机架:

PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.awareness.attributes": "rack_id",
    "cluster.routing.allocation.awareness.force.rack_id.values": ["rack1", "rack2", "rack3"]
  }
}

配合节点属性:node.attr.rack_id: rack1 配置,ES 会确保同一索引的主副分片不会落在同一个 rack 上。

小结

分片大小和数量是 ES 集群设计的"地基"。对大多数场景,单分片 30GB、每节点最多 600 个分片 是安全舒适区。一旦超出就要用 reroute 和 total_shards_per_node 等机制主动管控,切忌等到出事再救火。