一粟 · yisu.app
· 13 min read
搜索

Elasticsearch 写入性能调优笔记

ES 写入一旦抖动,整个搜索业务都会受影响。本文从 refresh interval、translog、bulk 调优等方面,整理一份能扛住大促的写入模板。

为什么 ES 写入是独立话题

很多团队用 ES 时把读和写混在一起调。结果是:当写入抖动、查询也跟着抖;或者反过来调高读参数,写入又出问题。ES 的写入和查询本质上走两条 pipeline,需要分开治理。

本篇就专注「写入」。我们关心的是:每秒写入量、写入延迟、磁盘 IO、refresh 行为、副本同步延迟。

refresh interval:不要让它来得太快

默认值是 1 秒,意味着每秒都会把 in-memory buffer 刷成新 segment。代价是:

  • segment 数剧增,merge 压力大。
  • 磁盘 IO 抖动。
  • 查询路径上的 segment 太多,搜索变慢。

写入大流量场景下,我们通常把 refresh_interval 拉到 30 秒甚至 60 秒:

PUT /logs-2026.07
{
  "settings": {
    "refresh_interval": "30s",
    "number_of_shards": 6,
    "number_of_replicas": 1
  }
}

代价是文档写入后到「可被搜到」有最多 30s 延迟。对日志、监控等业务完全可以接受。

translog:是 ES 写入稳定性的关键

translog 是 ES 写入的「wal」。配置项有几个关键参数:

  • translog.durability:async / request。request 模式下每次 write 都会 fsync,是安全但慢;async 模式批量 fsync,性能好但崩了会丢数据。
  • translog.flush_threshold_size:默认 512mb,达到后强制 flush。
  • index.translog.interval:translog sync 间隔。
在多副本与高写入场景下,async + 较大 flush threshold 通常更平衡。真正关键的写入走 request。

Bulk 调优:批大小不是越大越好

很多新手以为「bulk 越大越好」。但事实是:

  • bulk 请求越大,server 端解析与内存压力越大。
  • 某个 doc 失败会导致整批部分成功,需要客户端重试。
  • 并发 bulk 太高,会把 heap 打爆。

经验值:每个 bulk 大小 5–15 MB,并发不超过 CPU 核数的两倍。

# Python 客户端示例:控制 batch 大小
from elasticsearch import Elasticsearch, helpers

es = Elasticsearch(hosts=[...])
actions = [
    {"_index": "logs-2026.07", "_source": {"msg": f"log {i}", "t": time.time()}}
    for i in range(5000)
]
helpers.bulk(es, actions, chunk_size=500, max_retries=2)

Shard 设计:写入视角的考量

主分片数量确定后基本不能改。对写入来说:

  • 分片不要过多(> 1000 会显著影响 cluster state)。
  • 单分片大小建议 10–50 GB。
  • 写入热点要避免落在某些分片上。

我们曾经有过一个 800GB 的「超大索引」,对 cluster state 是负担。改成「按时间滚动 + ILM」,把老数据自动 shrink 到只读层。

Routing 和写入热点

默认按 doc id 路由到分片。如果业务有明显热点,可以让业务侧提供一个 routing key,让同主题 doc 集中到少数分片,对 schema 内聚合查询更友好:

POST /logs-2026.07/_doc?routing=user_42
{ "msg": "...", "user_id": 42 }

Mapping 设计:写入要尽量「扁」

几个写入友好原则:

  • 关闭不需要的字段类型识别(dynamic strict)。
  • 关掉不必要的 _source 字段(不需要回读时)。
  • 使用 keyword 而不是 text + keyword 除非要分词。

节点规划与 IO 模型

写入节点最好是「专用数据节点」,与查询节点分离。磁盘推荐 NVMe SSD,因为 translog fsync 与 segment merge 都对随机写有要求。

磁盘适用风险
NVMe SSD写入密集
SATA SSD中等IO 抖动
云 EBS弹性场景延迟方差大

写入限流与回压

当 ES 出现写入拒绝(HTTP 429),不要毫无防护地加重试。常见做法:

  1. 客户端检测 429,按指数退避重试。
  2. 服务端打开 index.write.wait_for_active_shards
  3. 业务层做「写入削峰」,在高峰期降级部分写入。

ILM:把模板变成可持续的策略

把写入调优变成「按时间阶段」自动执行:

policy:
  hot:
    - rollover: 50GB or 7d
  warm:
    - forcemerge: 1
    - shrink: 1
  cold:
    - freeze
  delete:
    - delete

通过 ILM 把「写入期 / 稳定期 / 归档期」按顺序切换,省得人手工操作。

监控与告警

  • indexing.index_total:每秒写入数。
  • indices.store.size:磁盘占用。
  • coordinating.write.rejected:被拒请求数。
  • merges.current:当前 merge 数。
  • translog.size:translog 体积。

把上面几个指标做成看板,告警阈值:写入延迟 P99 > 5s、内存使用 > 75%、拒绝 > 0。

ES 8 / OpenSearch 的几个差异

  • ES 8 默认安全模式开启,需要先做 TLS / 用户配置。
  • OpenSearch 提供更多企业级插件。
  • 写入侧没大差异,但文档中心、ILM 行为有部分区别。

给团队的简短清单

  • 写入与查询分离视角调优。
  • refresh_interval 不要 1s。
  • translog durability 按业务分层。
  • bulk 控制在 5–15 MB。
  • 分片不要过度拆分。
  • ILM 自动化生命周期。
  • 监控写入相关的 5 个核心指标。

ES 写入看上去是个配置问题,本质是对「写入路径」各环节开销的理解。把每一条链路看清楚,把它做成可持续的调优。这就是工程。