1. 客户端请求与路由
- 请求入口:客户端通过 REST API(如
PUT /{index}/_doc/{id})或 Transport Client 发送文档写入请求。 - 路由计算:根据文档的
_id(或自定义路由参数)计算目标分片:
shard = hash(_routing) % number_of_primary_shards
- 文档会被路由到对应的主分片(Primary Shard) 。
- 若未指定
_id,Elasticsearch 会自动生成唯一 ID(基于 UUID),并跳过版本冲突检查。
2. 主分片写入
主分片所在节点处理写入请求,步骤如下:
(1) 内存写入(In-Memory Buffer)
- 文档首先写入 内存缓冲区(In-Memory Buffer) ,此时数据不可被搜索。
- 同时,操作日志(Translog)被同步记录到磁盘(默认
index.translog.durability为request,即每次请求都持久化)。
(2) Refresh 操作
-
触发条件:默认每隔 1秒(可通过
index.refresh_interval配置)或缓冲区达到阈值。 -
操作内容:
- 将内存缓冲区的数据生成新的 Lucene Segment,并写入文件系统缓存PageCach(非磁盘)。
- 新 Segment 被打开后,文档可被搜索(近实时搜索,NRT)。
-
影响:频繁 Refresh 会生成大量小 Segment,增加后续合并(Merge)压力。
3. Translog 持久化
-
Translog 作用:确保在内存数据未持久化到磁盘时,仍可通过 Translog 恢复数据。
-
持久化策略:
request模式:每次写入请求后同步刷盘(强一致性,性能较低)。async模式:异步定期刷盘(更高吞吐,但可能丢失部分数据)。
-
Flush 操作:
-
当 Translog 大小达到阈值(默认 512MB,
index.translog.flush_threshold_size)或时间间隔(默认 30分钟),触发 Flush:- 将内存缓冲区数据写入磁盘,生成新的 Lucene Segment。
- 清空 Translog,创建新的 Translog 文件。
-
4. 副本分片同步(Replication)
主分片写入成功后,并行将操作同步到所有副本分片(Replica Shards) :
-
副本分片执行相同的写入流程(内存缓冲区 + Translog)。
-
写入一致性控制:
wait_for_active_shards:可配置需等待的副本分片数(如all、1)。- 默认仅需主分片成功即可返回响应(
consistency级别为quorum)。
5. Segment 合并(Merge)
-
后台任务:定期合并多个小 Segment 为更大的 Segment,优化查询性能。
-
合并策略:
- Tiered Merge Policy(默认):分层合并,优先合并大小相近的 Segment。
- Log Byte Size Merge Policy:基于 Segment 总大小的合并。
-
影响:
- 合并过程消耗 I/O 和 CPU 资源,可能影响写入性能。
- 合并后删除旧 Segment,释放磁盘空间。
6. 最终持久化(Fsync)
- 手动触发:调用
_flushAPI 或满足特定条件时,强制将文件系统缓存中的数据写入磁盘。 - 作用:确保数据持久化,但通常依赖 Translog 的容错机制即可。
写入流程优化技巧
1. 提升吞吐量
- 批量写入:使用
_bulkAPI 减少网络开销。 - 调整 Refresh 间隔:增大
refresh_interval(如30s)减少 Segment 生成频率。 - 关闭副本:写入时临时设置
index.number_of_replicas: 0,写入完成后再恢复。
2. 降低延迟
- 强制 Refresh:写入后调用
_refresh使数据立即可查。 - 缩短 Translog 刷盘间隔:调整
index.translog.sync_interval
3. 容错与一致性
- 设置
wait_for_active_shards:确保足够副本分片写入成功。 - 启用 Translog 校验:
index.translog.durability: request避免数据丢失
写入流程图
总结
Elasticsearch 的写入流程通过内存缓冲区、Translog、近实时搜索等机制,在性能与可靠性之间实现了平衡。理解其核心步骤(内存写入、Translog、Refresh、副本同步、Segment 合并)及可调参数,是优化写入性能和处理异常场景的关键。