Press "Enter" to skip to content

记一次 Elasticsearch 的写入性能优化

1. 背景与问题现象

最近我们的日志系统遇到了严重的写入瓶颈。架构链路为:应用系统 -> MQ -> Logstash -> Elasticsearch

ES 集群配置如下:

  • 节点规模:3 节点
  • 存储架构网络存储 (NAS)
  • 索引策略:按天切分,默认 1 分片 1 副本
  • 数据规模:单节点存量数据约 30TB+

问题现象:
随着业务量增长,我们发现 MQ 消息积压严重。通过监控发现一个诡异的现象:

  • 应用端写日志的流量仅仅 10MB/s
  • ES 服务器端的网络流量却飙升到了 100MB/s
  • 写放大高达 10 倍!

在尝试扩容之前,我们决定先深入排查,看是否能通过配置优化解决。

2. 排查过程

2.1 检查节点负载

通过 _cat/nodes 查看集群状态,发现虽然 CPU 使用率没有打满(约 20%-50%),但 Load Average(负载)极高,部分节点甚至高达 45。

GET /_cat/nodes?v&h=name,cpu,load_1m,dt,du
name           cpu  load_1m   dt     du
node-3         18   45.84     40tb   33tb  <-- 异常高负载
node-1         22   24.03     40tb   31.4tb

低 CPU 高 Load,通常意味着 IO 等待(IO Wait) 严重。

2.2 抓取热点线程

使用 _nodes/hot_threads 进一步定位 ES 在忙什么,发现了核心线索:

100.0% [cpu=38.1%, other=61.9%] (500ms out of 500ms) cpu usage by thread 'elasticsearch[...][Lucene Merge Thread]'
  • Lucene Merge Thread:ES 正在疯狂进行段合并(Segment Merge)。
  • other=61.9%:线程有 62% 的时间处于非 CPU 计算状态。结合 NAS 存储环境,这意味着线程大部分时间都在等待网络存储的 IO 响应

3. 根因分析:为什么 NAS 上流量会放大 10 倍?

经过分析,我们确认问题的根源在于 Elasticsearch 的默认配置与网络存储(NAS)特性不兼容

  1. 频繁的 Segment Merge(段合并)
    ES 默认 refresh_interval 为 1秒。这意味着每秒都会生成一个新的小文件(Segment)。在 NAS 环境下,ES 后台线程需要不断地通过网络把这些小文件读出来,合并成大文件,再通过网络写回去。这产生了巨大的双向网络流量

  2. 同步刷盘(Translog Fsync)
    ES 默认 translog.durabilityrequest。即每次写入请求都要强制刷盘(fsync)。在本地盘上这很快,但在网络存储上,网络延迟会导致严重的写入卡顿,并产生大量小包 IO。

  3. 分片分布不均
    原配置为 1 主分片 1 副本,导致 3 个节点的集群中,始终有一个节点在“围观”,没有分摊写入压力。

4. 优化方案

在不增加硬件投入的前提下,我们调整了索引模板(Index Template)和 ILM(生命周期管理)策略。

4.1 核心配置调整(针对 NAS 优化)

我们将默认配置修改为以下“写优化”模式:

{
  "index": {
    "lifecycle": {
      "name": "LOG-POLICY-PROD"
    },
    // 1. 增加分片数:充分利用3个节点的并发写入能力
    "number_of_shards": "3",
    
    // 2. 延长刷新间隔:从 1s 改为 60s
    // 减少 Segment 生成频率,大幅减少后台合并产生的网络流量
    "refresh_interval": "60s",
    
    // 3. 异步刷盘:这是在 NAS 上提升性能的关键
    "translog": {
      "durability": "async",  // 改为异步,不再每次请求都强制刷盘
      "sync_interval": "30s"  // 每30秒批量刷盘一次,合并 IO
    }
  }
}

配置解读:

  • 收益:将大量微小的随机 IO 合并为少量的大块顺序 IO,极大地降低了 NAS 的通信开销。
  • 代价refresh_interval: 60s 意味着日志写入后可能有 1 分钟延迟才能被搜到;async 意味着极端宕机情况下可能丢失最近 30 秒数据。对于日志业务,这是完全可接受的权衡。

4.2 历史数据治理(ILM 策略)

针对 30TB 的存量数据,我们优化了 ILM 策略,开启 Warm Phase 并执行 Force Merge

  • 操作:将 2 天前的索引强制合并为 1 个 Segment(max_num_segments: 1)。
  • 目的:彻底停止对老索引的后台合并操作,将所有网络带宽留给当天的热点写入。

5. 实施与验证

由于修改模板只对新索引生效,为了解决当天的积压问题,我们手动对正在写入的索引应用了上述 Settings 更新。

优化后的效果立竿见影:

  1. 网络流量下降:ES 服务器网卡流量从 100MB/s 骤降至 30-40MB/s,写放大现象基本消除。
  2. 负载降低:节点的 Load Average 从 40+ 逐步回落到正常水平,IO Wait 消失。
  3. 积压清理:MQ 的积压消息在短时间内被消费殆尽,系统恢复实时写入。

6. 总结

在基于 网络存储(NAS/SAN) 部署 Elasticsearch 时,千万不能使用官方默认配置。

  • 默认配置(1s 刷新、同步刷盘)是为 本地 SSD 设计的,追求极致的数据可靠性和实时性。
  • NAS 场景下,必须通过 “空间换时间”“批量换低频” 的策略(加大 buffer、延长 refresh、异步 translog)来规避网络 IO 的高延迟瓶颈。

这次优化证明,在达到硬件瓶颈之前,合理的参数调优往往能带来数倍的性能提升。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注