Elasticsearch 的 Write-Ahead Log(Translog)学习笔记
非常好,这是一个高级系统设计问题。我们可以从《Designing Data-Intensive Applications(DDIA)》的视角,系统地解析 Elasticsearch 的 Write-Ahead Log(WAL)机制及其相关抽象组件。
🧩 一、先复盘:DDIA 视角下的“存储抽象层次”
在 DDIA 的体系中,持久化与一致性的基础抽象一般分为:
| 层次 | 关键概念 | 典型组件 |
|---|---|---|
| 1️⃣ Commit Log / WAL | 记录操作意图(append-only),保证crash后能恢复 | Kafka、RocksDB WAL、Postgres WAL、Elasticsearch translog |
| 2️⃣ In-memory Buffer / MemTable | 暂存最近写入的索引/数据,用于快速查询与批量刷盘 | Lucene Segment Writer, MemTable |
| 3️⃣ Segment File / SSTable | 不可变的排序文件,用于合并与压缩 | Lucene Segment, SSTable |
| 4️⃣ Compaction / Merge | 周期性合并旧数据文件以清理过期项 | Lucene Merge Policy, LSM Compaction |
| 5️⃣ Index Metadata / Checkpoint | 标记一致性边界,方便恢复 | ES Commit Point, Checkpoint file |
Elasticsearch 基本完全符合这套结构,只不过它在 Lucene 之上再包了一层分布式协调逻辑。
🧠 二、Elasticsearch 的 WAL 实现:Translog
在 ES 中,WAL 对应的组件叫 Translog(Transaction Log)。
🔹 主要职责
-
持久化写入请求(WAL功能): 每当一个 document 被索引(index/update/delete)时,写操作先进入内存 buffer(in-memory index buffer),同时在 translog 记录一条日志。
-
崩溃恢复: 如果节点宕机,重启时会从 translog 读取并重放未持久化的操作。
-
异步落盘优化: 为了性能,ES 支持配置
index.translog.durability:"request"(默认)→ 每个请求 fsync 一次;"async"→ 定期批量 fsync;"flush"→ 定期 checkpoint 后清空旧 translog。
🔹 写入流程(结合 Lucene)
下面是 Elasticsearch 写入一条文档的完整路径:
Client Request
↓
Primary Shard
↓
Index Buffer (in-memory, Lucene RAM buffer)
↓
Translog (append-only WAL file)
↓
Lucene Segment (flush时写出新segment)
↓
Commit Point (记录索引一致性)
流程分解如下:
| 步骤 | 描述 |
|---|---|
| 1️⃣ | 请求到达主分片(Primary Shard),被写入 Index Buffer(Lucene RAM buffer)。 |
| 2️⃣ | 同时生成一条操作日志写入 Translog 文件(append-only)。 |
| 3️⃣ | 每隔一段时间或 buffer 满时执行 flush:将内存索引转换为新的 Lucene Segment 文件(不可变)。 |
| 4️⃣ | flush 成功后,生成新的 commit point 并截断旧的 translog。 |
| 5️⃣ | 数据同步到 replica shard,由 replica 重放同样的写操作。 |
⚙️ 三、Translog 的物理结构
每个分片都有自己的 translog 文件,例如:
/data/nodes/0/indices/<index_uuid>/<shard_id>/translog/
├── translog-123456.tlog
├── translog-123457.ckp
└── translog-generation
组成部分:
| 文件 | 作用 |
|---|---|
.tlog | 主体日志文件(append-only),包含操作记录 |
.ckp | checkpoint 文件,标记已 fsync 的位置 |
translog-generation | 记录当前 translog 序号 |
每条日志 entry 一般包含:
- Operation type(index / delete / no-op)
- seq_no(全局顺序号)
- docID
- source data(原始 JSON)
- version
🔄 四、与 Lucene Segment 的配合:Flush 与 Commit
Lucene 的数据结构是 immutable segment,而 Elasticsearch 的实时性需求要求它支持近实时搜索(NRT)。 因此采用如下折中机制:
| 阶段 | 作用 |
|---|---|
| refresh(默认 1s 一次) | 将内存 buffer 的数据转入 Lucene 的新 segment(仅在内存/FS cache 层,不fsync)——提供近实时搜索。 |
| flush | 将所有 segment 落盘(fsync)+ 提交 commit point + 清理旧 translog——提供持久化。 |
这正对应 DDIA 中描述的两种 durability 层次:
- log-first (WAL) 保障崩溃一致;
- segment merge + commit 保障长期持久化。
🧬 五、分布式一致性:Primary-Replica 复制与 WAL 传播
在分布式层面,Elasticsearch 的写入机制延伸出类似于 DDIA 第9章的 Replication Log 概念:
-
写入主分片(Primary):
- Primary 写入 translog;
- 生成 seq_no 和 primary_term;
- 将操作转发给所有 replica。
-
Replica 重放日志(Replay):
- Replica 节点写入自己的 translog;
- 确认成功后返回 ack。
-
Primary 等待所有 ack 后:
- 标记请求成功(可配置 quorum 写策略)。
这个过程类似于 DDIA 所述的 “主从复制日志(Replication Log)” 模式——本质上,translog 既是本地 WAL,又是分布式复制的源。
🧱 六、DDIA 对应章节映射
| DDIA 章节 | 核心思想 | 在 Elasticsearch 中的体现 |
|---|---|---|
| 第3章:存储与索引结构 | 日志结构存储(Log-structured storage)+ SSTable | Lucene segment + translog |
| 第5章:复制 | 单主复制 + WAL 传播 | Primary/Replica 写入流程 |
| 第6章:分区 | 分片(Shard)为独立数据子集 | 每个 shard 自带 WAL & Lucene |
| 第7章:事务 | 基于操作序列号和 term 实现幂等与顺序一致性 | seq_no + primary_term |
| 第9章:一致性与复制 | Leader-based replication with logs | Translog 即 Leader log |
| 第10章:批处理 | segment merge = compaction | Lucene MergePolicy |
🔍 八、简短总结
Elasticsearch 的 translog 就是 DDIA 所说的“日志结构存储体系中的 Write-Ahead Log”。 它与 Lucene 的 immutable segment 形成“日志 + 快照”的双层模型:
- 日志层(translog) → 负责可恢复性和复制;
- 快照层(segment) → 负责可搜索性与压缩。
这种设计结合了高写入吞吐(append-only)、快速恢复(log replay)、高查询性能(segment-based index),是典型的 LSM + Log Replication 混合架构。
🔍 九、总结与洞察
Elasticsearch 的 Translog = DDIA 所说的 “Commit Log / Write-Ahead Log”。
它与 Lucene Segment 构成了 “日志 + 快照” 的双层模型:
| 层次 | 职责 |
|---|---|
| 🧾 Translog (日志层) | 提供崩溃可恢复性与复制保障 |
| 📚 Lucene Segment (快照层) | 提供高效可搜索性与压缩能力 |
这种架构融合了:
- 高写入吞吐(append-only)
- 快速恢复(log replay)
- 高查询性能(segment-based inverted index)
是典型的 LSM + Log Replication 混合体系, 完美印证了 DDIA 中的存储与复制设计原则。
📖 参考资料
-《Designing Data-Intensive Applications》,Martin Kleppmann
- Elasticsearch 官方文档: Translog
- Lucene 文档: Segments and Commits
- DDIA 中文版:第3章、第5章、第9章、第10章
- Blog: Deep Dive into Elasticsearch Translog and Commit Process
🧭 延伸阅读建议
探索:如何在分布式系统中实现 “WAL + Snapshot” 一致性(对比 Kafka log + checkpoint)
思考:为什么 Elasticsearch 不使用传统的 page-based 存储结构?
对比:RocksDB 的 WAL + MemTable + SSTable 三层架构与 ES 的异同