3012 字
约 10 分钟
18
S13-A06 · 搜索集群运维决策:分片、副本与容量

搜索集群的运维决策有个残酷特征:分片数在索引创建时写下,之后基本改不了。要回答的不是"怎么调",而是"一开始按什么口径定、事后用什么代价补救"。下面按分片 → 副本 → 容量 → 角色 → 索引设计 → 查询归因 → 变更窗口给出代价结构与失效边界。参数按 Elasticsearch 7.x/8.x 通用语义,默认值按所部署版本核对。


1. 分片决策的三个维度与硬约束

维度 决定什么 估算输入
数据量 分片数量下限 单分片目标大小 × 索引总数
查询并发 分片数量上限(扇出成本) 每秒查询数、可用线程、单分片延迟
写入吞吐 分片数量下限(并行度) 每秒文档数、单分片写入能力
PUT /logs-2026.11
{
  "settings": {
    "number_of_shards": 6,
    "number_of_replicas": 1,
    "refresh_interval": "10s"
  }
}

硬约束:number_of_shards 只能靠 _shrink / _split / _reindex 改动,三者都要重写数据。定分片数要按未来一年数据量算,为当前量优化是这类集群最常见的错误。


2. 分片过多与过少的代价

方向 代价 表现
过多 堆内存 每分片有独立段元数据与缓冲,堆压力随分片数线性上升
过多 文件句柄 段文件 × 分片数,容易撞 ulimit/系统上限
过多 查询扇出 一次查询打到所有分片,协调节点成为瓶颈
过多 恢复时间 节点重启后分片并行恢复,集群长时间处于 yellow/red
过少 并行度不足 单分片写入与查询无法横向扩展
过少 再平衡粒度粗 扩容时无法把负载均摊到新节点
# 用这两个接口判断现状:分片规模分布 与 单分片大小
GET _cat/shards/logs-*?v&h=index,shard,prirep,store,node
GET _cat/indices/logs-*?v&h=index,pripri.store.size,docs.count,pri

判断口径:单分片目标量级通常落在数十 GB 区间(常见经验 30–50GB,按版本与硬件核对)。若某索引分片数远超数据量所需,先合并而不是先加节点。


3. 副本:它能做什么,不能做什么

副本解决的问题 副本不解决的问题
读请求横向扩展 写入吞吐上限(写要复制到每个副本)
节点故障时数据可用 单分片写入热点
滚动升级期间的服务连续 段合并与刷盘的 IO 压力
查询失败重试的其他选择 深分页与高基数聚合的成本
PUT /logs-2026.11/_settings
{ "index": { "auto_expand_replicas": "0-1" } }

auto_expand_replicas 适合节点数会变的集群,可避免"节点少副本多 → 长期 yellow";但副本数变化会触发分片迁移,IO 与网络受影响,变更放低峰。


4. 容量估算:堆、数据与合并开销

估算项 口径 误差来源
数据总量 单文档字节 × 文档数 × (1 + 副本数) 字段映射膨胀、_source 重复存储
堆需求 与分片数、段数、聚合并发相关 大聚合与脚本查询瞬时占用
合并开销 写入期持续产生,与段数相关 批量导入时合并压力集中
预留 磁盘留 15%–20%,堆不超物理内存一半 预留被临时索引吃掉
分片数下限 = ceil(预期数据量 / 单分片目标大小)
分片数上限 = min(节点数 × 每节点可承载分片数, 查询并发可承受扇出)
堆与段数正相关,按实测曲线核对

不要用"堆:数据 = 1:30"这类固定比例决策,它只在特定查询形态下成立,聚合与脚本多的集群明显偏离。


5. 节点角色划分

角色 职责 资源特征
数据节点 存分片、执行查询与写入 磁盘与堆并重,IO 敏感
协调节点 扇出、合并结果 CPU 与网络,堆中等
主节点 集群状态管理 稳定低延迟,不与数据节点混部
预处理节点 ingest pipeline CPU,易被大文档拖慢
# elasticsearch.yml 角色声明(按版本核对可用角色名)
node.roles: [ data_hot, data_content ]
# 专用协调节点
# node.roles: [ ]
# 专用主节点(奇数个,3 个起)
# node.roles: [ master ]

角色分离的收益是故障域隔离:大聚合打满协调节点不会连带把选主拖进无响应。代价是节点数与跨节点流量上升,小集群分离反而降低利用率。


6. 索引设计的取舍

方案 适用 代价
按时间分索引 日志、事件流,可整索引删除 索引数量多,分片总数上升
按业务分索引 数据保留期一致、查询域清晰 单索引持续增长,删除只能按文档
单索引多 type 已不可用(7.x 起移除) —
PUT _ilm/policy/logs-hot-warm
{
  "policy": {
    "phases": {
      "hot":    { "actions": { "rollover": { "max_primary_shard_size": "40gb" } } },
      "warm":   { "min_age": "7d",  "actions": { "forcemerge": { "max_num_segments": 1 } } },
      "delete": { "min_age": "90d", "actions": { "delete": {} } }
    }
  }
}

按时间分索引的价值不在查询快,而在删除是元数据操作:删索引瞬时完成,按文档删除会产生大量 deleted docs 并触发合并。


7. refresh、段合并与 deleted docs

设置 提高 降低 建议
refresh_interval 增大 写入吞吐 数据可见延迟 批量导入期临时设 -1 或 30s
频繁 forcemerge 查询速度(段少) 磁盘 IO、CPU,且产生大段 只在只读索引上做
大量按文档删除 — 查询效率、磁盘 改用整索引删除或 ILM
# 批量导入:先关刷新,导完再恢复并 forcemerge
PUT /bulk-index/_settings { "index": { "refresh_interval": "-1" } }
POST /bulk-index/_forcemerge?max_num_segments=1
PUT /bulk-index/_settings { "index": { "refresh_interval": "1s" } }

deleted docs 堆积的信号是"文档数没涨,磁盘和查询延迟都在涨"。它不会自动消失,只能靠合并回收——这也是为什么"按天删索引"比"按天删文档"健康得多。


8. 查询性能的常见归因

症状 常见原因 验证方式
越翻越慢 深分页(from + size 累积) 对比 from=0 与 from=9000 的耗时
个别查询打满 CPU 脚本、正则、通配前缀 profile API 看各阶段耗时
聚合慢 高基数字段聚合、嵌套聚合 降基数或改用预聚合
全集群变慢 分片过多导致扇出、协调节点瓶颈 对比单分片查询与全索引查询
GET /logs-*/_search
{ "profile": true, "query": { "match": { "msg": "timeout" } } }

归因顺序:先分清单查询慢还是集群整体慢。前者看语句与映射,后者看分片数、段数、协调节点与合并线程。混在一起排查,通常得到"加机器"这种最贵的结论。


9. 扩容、再平衡与滚动升级

# 迁移限速:避免迁分片打满网络与磁盘
PUT _cluster/settings
{ "persistent": {
    "cluster.routing.allocation.node_concurrent_recoveries": 2,
    "indices.recovery.max_bytes_per_sec": "80mb"
} }
操作 触发时机 业务表现
加分片再平衡 节点容量不均、热点集中 迁移期 IO 上升、查询延迟抬升
滚动升级 版本修复、配置变更 每次重启一个节点,副本顶上
主节点替换 主节点不稳 选主期间集群状态短暂不可写

升级顺序:先专用主节点(选举稳定)→ 协调节点 → 数据节点。每步验证三件事:健康色、_cat/shards 无长期 RELOCATING、写入拒绝归零。跳过验证直接推进,是把小问题叠成事故的标准路径。


10. 监控指标与成本优化

指标 位置 阈值形态
堆压力 _nodes/stats/jvm 老年代持续 > 75% 且 GC 频繁
段数 _cat/segments 单分片段数长期偏高
合并线程 _nodes/stats/indices/merge 合并队列积压
写入拒绝 _nodes/stats/thread_pool rejected 持续增长
慢查询 慢日志 按 P99 设阈值
成本手段 收益 代价
冷热分层 冷数据用大容量低配盘 查询冷数据延迟上升
压缩(index.codec: best_compression) 磁盘下降 写入与查询 CPU 上升
快照归档后删除 存储最省 恢复需要时间与流程
降低副本数 存储减半 可用性与读扩展下降

成本优化优先级:先删无用数据与索引,再谈压缩与分层。多数存储浪费来自没人敢删的历史索引,而不是编码效率。


11. 排错:六种集群异常

现象 原因 处理
长期 yellow 副本无处分配(节点数不足) 降副本或加节点,查 allocation 过滤
分片无法分配 磁盘水位超过低水位线 清理或调整 watermark,先看 _cluster/allocation/explain
写入被拒 线程池队列满、合并跟不上 降批量并发,加 refresh 间隔,查合并压力
查询突然变慢 合并/快照/迁移争抢 IO 看 _cat/thread_pool
重启后长时间红 并发恢复低、磁盘慢 提并发恢复数
磁盘只涨不降 deleted docs 未合并 forcemerge 只读索引

排查入口固定为一句:GET _cluster/allocation/explain——多数"分片不分配"的答案在这里,不在日志里。


12. 边界:这些情况不必上集群

场景 判断 理由
数据量小(单机可容纳) 单分片、单节点 分片与副本只增加复杂度
写入远大于查询 优先考虑别的存储 搜索引擎的写入成本高于日志型存储
查询以主键精确匹配为主 用 KV/关系库 倒排索引的收益体现不出来
强事务与实时一致 关系库 + 缓存 搜索引擎的近实时语义不满足
检索需求简单(前缀、模糊) 数据库索引即可 引入集群的运维成本不划算

搜索引擎的定位是检索与聚合,不是通用存储。当主库用,代价会以"数据丢失窗口 + 重建时间"的形式出现在事故里。


13. 自测

  1. 为什么分片数要按未来一年的数据量定,而不是当前量?
  2. 分片过多的四类代价里,哪一类在节点重启时才暴露得最明显?
  3. 副本能扩展读,为什么不能扩展写?
  4. deleted docs 堆积的信号是什么?为什么按索引删除比按文档删除健康?
  5. 集群整体变慢与单查询变慢,排查入口有什么不同?
  6. 滚动升级时先升哪类节点,每一步验证哪三件事?
S13-A06 · 搜索集群运维决策:分片、副本与容量
https://www.clxhxhhr.top/posts/6073/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。