1. 引言:为什么文档不可变?
Elasticsearch(ES)作为一款高性能的分布式搜索引擎,其底层存储引擎基于 Apache Lucene,而 Lucene 的核心设计之一就是 文档不可变性(Immutable Documents)。这种设计带来了显著的性能优势:
- 并发安全:无需加锁即可支持高并发读写。
- 缓存友好:倒排索引等数据结构一旦生成便不会被修改,可长期驻留内存。
- 原子性保证:每个文档的版本独立,避免部分更新导致的数据不一致。
但这也引出一个关键问题:如何实现文档的更新和删除?
本文将深入剖析 ES 的更新与删除流程,揭示其背后的高效设计。
2. 删除操作:逻辑删除与物理删除
2.1 逻辑删除(标记删除)
当执行删除操作时,ES 不会立即物理删除文档,而是通过以下步骤实现逻辑删除:
- 标记删除:在对应段的
.del
文件(删除标记文件)中记录该文档的_id
,将其状态置为deleted
。 - 查询过滤:后续搜索时,ES 会检查
.del
文件,自动过滤掉已标记删除的文档。
示例:
Segment_1: [DocA, DocB, DocC]
.del文件: [DocB] → 实际搜索结果仅返回 [DocA, DocC]
2.2 物理删除(段合并时触发)
逻辑删除的文档会一直占用磁盘空间,直到触发 段合并(Segment Merge):
- Merge 过程:多个小段合并为一个大段时,标记为
deleted
的文档会被物理丢弃,不再写入新段。 - 资源释放:合并完成后,旧段及其
.del
文件被删除,磁盘空间得到回收。
关键点:删除操作的性能瓶颈在于段合并频率,高频删除场景需优化
merge
策略(如调整merge.policy
)。
3. 更新操作:删除+新增的组合拳
由于文档不可变,ES 的更新操作实际上是 两步操作:
- 标记旧文档为删除:在原段的
.del
文件中记录旧文档_id
。 - 写入新文档:新文档被索引到一个新段中,
_version
值递增。
示例:
更新 DocB(旧内容为 {"name": "Alice"} → 新内容 {"name": "Bob"}):
1. Segment_1 的 .del 文件标记 DocB 为 deleted。
2. 新文档 DocB:{"name": "Bob"} 写入 Segment_2。
3.1 版本控制与冲突解决
ES 通过 _version
和 _seq_no
+ _primary_term
实现乐观锁:
- 如果并发更新同一文档,高版本会覆盖低版本,避免数据冲突。
- 使用
if_seq_no
和if_primary_term
可实现 CAS(Compare-And-Swap)操作。
4. 段(Segment)的生命周期管理
4.1 从 Memory Buffer 到 Segment
- Refresh:默认每 1秒 将内存中的
Memory Buffer
数据刷新为一个新段(写入文件系统缓存,非磁盘)。- 此时数据可被搜索,但尚未持久化(断电可能丢失)。
- Flush:当
Translog
(事务日志)达到阈值(默认 512MB)或间隔 30 分钟,触发 Flush:- 将文件系统缓存中的段持久化到磁盘。
- 清空已持久化的
Translog
。
4.2 段合并(Merge)的优化
- 合并策略:默认使用
TieredMergePolicy
,优先合并大小相近的段。 - IO 与 CPU 权衡:
- 频繁合并 → 减少段数量,提升查询性能,但增加 IO 压力。
- 低频合并 → 节省 IO,但查询需扫描更多段。
调优建议:根据业务场景调整
index.merge.policy
参数(如max_merged_segment
)。
5. 事务日志(Translog):数据安全的守护者
所有写操作(增删改)会先写入 Translog
,再写入内存缓冲区,确保:
- 宕机恢复:重启后通过重放
Translog
恢复未持久化的数据。 - 数据一致性:只有
Translog
对应的操作被持久化后,才会清除日志。
6. 新版改进:从 .del 文件到软删除(Soft-Deletes)
在 ES 7.0+ 中,删除标记机制优化为 软删除:
- 不再依赖单独的
.del
文件,而是通过 Lucene 的 存活文档位图(.liv 文件) 管理。 - 支持更高效的 跨段版本控制,适合频繁更新的场景。
7. 最佳实践
- 避免高频更新:更新会生成新文档,导致段膨胀,影响查询性能。
- 合理配置 Merge:针对写多读少或读多写少场景调整合并策略。
- 监控 Translog:避免
Translog
过大引发长时间恢复。
总结
Elasticsearch 通过 逻辑删除 + 段合并 的机制,在保持文档不可变性的同时,实现了高效的更新与删除功能。理解这一设计,有助于优化索引性能并规避潜在问题。
如需获取更多关于 Elasticsearch 核心原理与实践技巧的内容,请持续关注本专栏《Elasticsearch 深度解析》系列文章。