ES之Translog丢失导致数据回退

"你说'几乎实时',ES 当真了 ------ 结果断电后它只记得'几乎'。"

------ 这不是直男,是异步写入的代价

💥 现象:重启后发现最近 5 分钟数据没了

  • 写入成功(返回 201 Created
  • 服务正常运行
  • 突然断电 / kill -9 / 宕机
  • 重启后:最近 5 分钟数据消失
  • 日志无报错,但 _count 明显少于预期

这不是 bug,是 index.translog.durability: async 的明确行为

🕵️ 根因:Lucene + Translog 的双缓冲机制

ES 写入流程(简化):
  1. 写入请求到达 → 写入 Translog(内存 buffer)
  2. 同时写入 Lucene Index(内存,不可搜索)
  3. 每秒 refresh → Lucene 变成 可搜索段(segment)
  4. 定期 fsync Translog 到磁盘(持久化)
  5. 定期 flush → Lucene 段落盘 + 清空 Translog
模式 行为 数据安全性 性能
request(默认) 每个写入请求都 fsync Translog ✅ 最高(断电不丢) ⚠️ 较低(每次 I/O)
async Translog 每 5 秒 fsync 一次 ❌ 最多丢 5 秒数据 ✅ 高(批量 I/O)

💡 默认就是 request 所以如果你没改过配置,不应该丢数据只有显式设为 async 才会丢

阶段 存储位置 是否持久化 是否可搜索 安全性
Translog (内存) 内存 ❌ 否 --- 断电丢失
Lucene (内存) 内存 ❌ 否 ❌ 否 断电丢失
Refresh → Segment 文件系统缓存 ❌ 否 ✅ 是 重启可能丢失
fsync Translog 磁盘 ✅ 是 --- 断电不丢(request 模式)
Flush 磁盘 ✅ 是 ✅ 是 完全持久化

之前也有过一个图片~ 各有千秋吧

为什么有人用 async
  • 写入吞吐要求极高(如日志场景)
  • 能容忍少量丢失("近实时"即可)
  • 但绝不能用于订单、支付、用户注册等关键业务!
配置 最大丢失窗口 实测吞吐(SSD)
durability: request 0(请求级持久化) ~8k writes/sec
durability: async ≤ 5 秒(默认 translog.sync_interval: 5s ~25k writes/sec

📊 压测结论(ES 8.10, i3.2xlarge):

  • async 吞吐提升 2--3 倍
  • 断电必丢最近 1--5 秒数据

🛠️ 正确解决方案:分层防护

第一层:关键索引强制 request(默认已满足!)
复制代码
# 创建索引时显式声明(虽默认已是 request,但显式更安全)
PUT /orders-2026
{
  "settings": {
    "index.translog.durability": "request",   // ← 默认值,可不写
    "index.translog.sync_interval": "5s"     // async 模式才生效
  }
}

GET /_settings?flat_settings=true&include_defaults=false # 查看 index.translog.durability

第二层:避免手动 flush
  • 不要频繁调用 POST /_flush
    • flush 会触发 Lucene commit + 清空 Translog
    • 如果刚 flush 完就断电,之后所有写入全丢(因为 Translog 已清)
  • 让 ES 自动 flush(默认条件:Translog > 512MB 或 30 分钟)
第三层:硬件 + OS 层加固
措施 作用
使用 带电容保护的 RAID 卡 防止 write cache 丢失
挂载磁盘加 data=ordered(ext4) 保证 metadata 与 data 顺序写
禁用磁盘 write cache(hdparm -W0 /dev/sda 强制物理落盘(性能下降,慎用)

💡 云环境注意 :AWS EBS / 阿里云云盘 默认提供 crash-consistent 保证 ,但 仍依赖 fsync

数据丢失后能否恢复?

❌ **不能!Translog 一旦丢失,数据永久消失。**丢了就是丢了,要学会为自己的行为买单单

为什么无法恢复?
  • Lucene 段文件只包含 已 refresh 的数据
  • Translog 是 唯一记录未 flush 写入的地方
  • 断电后内存中 Translog buffer 清零 → 无任何痕迹
唯一出路:从上游重放
  • 订单系统:从 MQ 重放消息
  • 日志系统:从 Filebeat 重新发送(需开启 persistent_queue
  • 用户操作:前端提示"请重试"

🛡️ 预防 > 补救!防不胜防咱也没办法,但是这是小小概率事件了

"你把 durability 设成 async,就像让朋友帮你记账却说'大概记得就行',结果他喝断片了---你的钱还在,只是 ES 不记得了。"

附1:如何安全地"提升吞吐"而不丢数据?

如果写入性能不够,不要改 durability,而是:

  1. 增加分片数(提高并行度)
  2. 用 bulk 批量写入(减少请求开销)
  3. 调整 refresh_interval (如 "30s",降低 refresh 频率)
  4. 升级 SSD / 增加内存(提升 I/O)

性能与安全可以兼得,前提是别碰 durability: async;万万不可

附件 1:Kibana Dashboard JSON ------ Translog 健康监控

监控这学问可大着嘞

指标 用途
indices.translog.size_in_bytes Translog 文件大小(突增可能预示 flush 延迟)
indices.translog.uncommitted_operations 未提交操作数(越高,断电丢失风险越大)
indices.refresh.total_time_in_millis refresh 耗时(影响写入吞吐)
thread_pool.write.rejected 写入线程拒绝(可能因 Translog I/O 阻塞)
导入方式(Kibana)
  1. 进入 Stack Management → Saved Objects → Import

  2. 上传下方 JSON 文件

    {"attributes":{"description":"Monitor Translog health to prevent data loss","kibanaSavedObjectMeta":{"searchSourceJSON":"{"query":{"language":"kuery","query":""},"filter":[]}"},"title":"ES Translog Health","version":1},"id":"es-translog-health","migrationVersion":{"dashboard":"8.0.0"},"references":[],"type":"dashboard"}
    {"attributes":{"columns":["indices.translog.size_in_bytes","indices.translog.uncommitted_operations"],"filters":[],"hideChart":false,"hideTotal":false,"indexPatternRefName":"pattern_0","isCompact":false,"minimumVisibleRows":5,"query":{"language":"kuery","query":""},"rowHeight":30,"sampleSize":1000,"sort":[{"columnId":"indices.translog.size_in_bytes","direction":"desc"}],"title":"Top Indices by Translog Size","viewMode":"list"},"id":"translog-size-table","migrationVersion":{"lens":"8.0.0"},"references":[{"id":"","name":"pattern_0","type":"index-pattern"}],"type":"lens"}
    {"attributes":{"aggregation":"avg","axisPosition":"left","axisType":"value","breakdownBy":"index","chartType":"line","colorMapping":{},"customLabel":"","dataLayer":"indices","fieldName":"translog.uncommitted_operations","filter":"
    ","groupBy":"terms","legendDisplay":"show","legendPosition":"right","metric":"avg","operationType":"average","palette":"default","panelTitle":"Uncommitted Operations (Risk of Data Loss)","reportDefinitions":{},"selectedMetricField":"translog.uncommitted_operations","seriesType":"normal","showGridlines":true,"showLegend":true,"timeInterval":"auto","title":"Uncommitted Operations Over Time","valueAxisScale":"linear","valueLabels":"none","visualizationType":"timeseries","xAxisScale":"time","yAxisExtents":[0,null]},"id":"uncommitted-ops-timeseries","migrationVersion":{"metrics":"8.0.0"},"references":[],"type":"metrics"}

💡 使用前提:叮咚咚

  • 已启用 Elastic Stack Monitoring(或 Metricbeat 收集 ES 指标)
  • 索引模式包含 .monitoring-es-*metricbeat-*

🔔 告警规则建议(基于此 Dashboard)

  • 条件indices.translog.uncommitted_operations > 100000 持续 5 分钟
  • 动作:通知 SRE ------ "高风险:断电可能导致大量数据丢失!"
相关推荐
武子康1 天前
大数据-240 离线数仓 - 广告业务 Hive ADS 实战:DataX 将 HDFS 分区表导出到 MySQL
大数据·后端·apache hive
洛森唛2 天前
ElasticSearch查询语句Query String详解:从入门到精通
后端·elasticsearch
字节跳动数据平台2 天前
5000 字技术向拆解 | 火山引擎多模态数据湖如何释放模思智能的算法生产力
大数据
武子康2 天前
大数据-239 离线数仓 - 广告业务实战:Flume 导入日志到 HDFS,并完成 Hive ODS/DWD 分层加载
大数据·后端·apache hive
洛森唛3 天前
Elasticsearch DSL 查询语法大全:从入门到精通
后端·elasticsearch
字节跳动数据平台3 天前
代码量减少 70%、GPU 利用率达 95%:火山引擎多模态数据湖如何释放模思智能的算法生产力
大数据
得物技术3 天前
深入剖析Spark UI界面:参数与界面详解|得物技术
大数据·后端·spark
武子康3 天前
大数据-238 离线数仓 - 广告业务 Hive分析实战:ADS 点击率、购买率与 Top100 排名避坑
大数据·后端·apache hive
武子康4 天前
大数据-237 离线数仓 - Hive 广告业务实战:ODS→DWD 事件解析、广告明细与转化分析落地
大数据·后端·apache hive
大大大大晴天4 天前
Flink生产问题排障-Kryo serializer scala extensions are not available
大数据·flink