news 2026/4/22 16:30:27

6.2 Elasticsearch-写入链路:Index → Refresh → Flush → Merge 源码走读

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
6.2 Elasticsearch-写入链路:Index → Refresh → Flush → Merge 源码走读

6.2 Elasticsearch-写入链路:Index → Refresh → Flush → Merge 源码走读

Elasticsearch 的写入链路是一条“先写内存、再写事务日志、后刷盘、最终合并”的四级流水线。
整条链路由Index → Refresh → Flush → Merge四个阶段组成,对应内核代码里的四个关键服务:
IndexServiceRefreshServiceFlushServiceMergeScheduler
下面以 8.11 分支源码为基准,按时间顺序把一次文档写入的完整旅程跑一遍,并给出可直接打断点的位置与核心字段含义。


1. Index:文档进门的第一站

入口TransportBulkAction#doExecuteBulkRequestHandler#executeBulk
关键类InternalEngine#index
关键字段versionMaplocalCheckpointmaxSeqNo

  1. 路由计算
    TransportBulkAction把 bulk 拆成 shard 级别的BulkShardRequest,通过IndexRouting#resolve得到目标主分片。
  2. 主分片写内存
    PrimaryShardOperation#doRunInternalEngine#index拿到Index操作对象,先加writeLock
  3. 版本号与序列号
    Index#resolveVersionversionMap做乐观锁冲突检测;SequenceNumbers#nextSeqNo原子递增生成seqNo
  4. 写 Lucene 的 DWPT
    IndexWriter#addDocument只是把文档追加到DocumentsWriterPerThreadpendingDocs队列,此时数据不可见
  5. 写 translog
    Translog#addIndex操作序列化后追加到translog.ckp文件,返回locationlocation被塞进Engine.Index结果里,用于回放。
  6. 主分片返回
    ReplicationOperationlocationReplicationRequest发到副本,副本走同一条InternalEngine#index路径,保证主副一致。

断点
org.elasticsearch.index.engine.InternalEngine:index第 952 行,assert seqNo > localCheckpoint.get();
可观察seqNoversionprimaryTerm三值是否连续。


2. Refresh:把内存数据变为可查

入口IndexService#refreshRefreshService#refresh
关键类ElasticsearchDirectoryReaderReadersAndUpdates
关键字段lastRefreshedCheckpointrefreshedSeqNo

  1. 触发条件
    • 定时:默认 1s (index.refresh_interval)。
    • 强制:调用_refreshAPI 或bulkrefresh=true
  2. 打开新 reader
    InternalEngine#refreshIndexWriter#getReader(true)拿到StandardDirectoryReader,内部是SegmentCoreReaders列表。
  3. 更新 checkpoint
    lastRefreshedCheckpoint被原子更新为当前已写完的最大seqNo只有 ≤ checkpoint 的文档才对 Searcher 可见
  4. 发布新的 Searcher
    IndexShard#storeNewSearcherDirectoryReader包装成ElasticsearchDirectoryReader,注册到ShardSearchRegistry;旧 reader 引用计数归零后自动关闭。

断点
org.elasticsearch.index.engine.InternalEngine:refresh第 1273 行,lastRefreshedCheckpoint = localCheckpointTracker.getProcessedCheckpoint();
可验证可见延迟 =localCheckpoint - lastRefreshedCheckpoint


3. Flush:把 translog 刷盘并提交 commit

入口FlushService#flushInternalEngine#flush
关键类TranslogWriterIndexWriter
关键字段flushSeqNocommittedTranslogGeneration

  1. 触发条件
    • translog 大小超过 512 MB (index.translog.flush_threshold_size)。
    • 定时 5 min 一次。
    • 重启前必须 flush(IndicesClusterStateService#applyClusterState)。
  2. 写 Lucene commit
    IndexWriter#commit()生成新的segments_N文件,把内存中所有已 refresh的段 fsync 到磁盘。
  3. 截断 translog
    Translog#current被关闭并重命名为<generation>.ckp,新 translog 从空文件开始;committedTranslogGeneration指针推进。
  4. 更新 shard state
    IndexShard#persistMetadatacommitSeqNocommitPrimaryTerm写进shard-state.st文件,供节点重启后恢复。

断点
org.elasticsearch.index.translog.Translog:closeIntoReader第 568 行,
logger.debug("flushing translog generation {}", generation);
可确认 flush 后旧 translog 文件是否被清理。


4. Merge:后台段合并与物理删除

入口MergeScheduler#mergeTieredMergePolicy#findMerges
关键类ElasticsearchMergePolicyMergeTask
关键字段maxMergedSegmentBytesdeletesPctAllowed

  1. 策略选择
    TieredMergePolicy把段按大小分层,优先合并大小相近且删除率高的段;maxMergedSegmentGB默认 5 GB,防止写出过大段。
  2. 合并流程
    IndexWriter#merge创建MergeSpecification,每个OneMerge包含待合并的SegmentCommitInfo列表;Lucene 把多个段读出 → 去删除 → 重新写成一个新段。
  3. 更新版本映射
    ElasticsearchMergePolicy#keepFullyDeletedSegment返回 false,保证完全删除的段在合并后直接物理丢弃;versionMap中对应uidversion被清理,防止版本泄露
  4. 并发控制
    合并线程与写入线程共享IndexWriterwriteLock,但merge本身使用flushLock的读锁,因此refresh 不会被 block,搜索仍可进行。

断点
org.apache.lucene.index.TieredMergePolicy:findMerges第 214 行,
if (segBytes > maxMergedSegmentBytes) continue;
可观察哪些段被选入合并列表。


5. 端到端时序图(简化)
Client ──► TransportBulkAction │ ├─► Index (DWPT + Translog) │ ├─► Refresh (1s 默认) ──► Searcher 可见 │ ├─► Flush (512 MB / 5 min) ──► Commit + Translog 清理 │ └─► Merge (后台) ──► 物理删除 + 段数量收敛

6. 调优提示与源码黑魔法
  1. refresh_interval = -1
    把 refresh 关掉后,lastRefreshedCheckpoint不再推进,搜索 0 结果,但写入吞吐可提升 30 %+;适合离线灌数据场景。
  2. translog durability = async
    异步刷盘每 5 s 一次,减少 fsync 次数,但宕机可能丢 5 s 数据;源码位置:TranslogWriter#syncBeforeReturn第 417 行。
  3. merge scheduler 限流
    MAX_MERGE_COUNT默认 3,MAX_THREAD_COUNT默认 1,机械盘可调到 2×2;源码位置:ElasticsearchMergeScheduler构造器。
  4. sequence number 回滚
    主分片失败时,新主通过Store#readLastCommittedSegmentsInfo读取commitSeqNo,把localCheckpoint回退到 commit 点,保证主副切换不丢数

7. 小结

Index 阶段只保证durability(translog),不保证visibility
Refresh 阶段把内存段暴露给 Searcher,是 ES 近实时搜索的精髓;
Flush 阶段把内存 + translog一起固化,是重启恢复的基石;
Merge 阶段把碎片段 + 删除文档持续整理,决定长期查询性能。

四段代码环环相扣,却通过lock-free checkpoint引用计数 reader做到几乎互不阻塞,值得反复走读。```
推荐阅读:
PyCharm 2018–2024使用指南

更多技术文章见公众号: 大城市小农民

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 11:35:49

PingFangSC字体解决方案:跨平台网页字体优化的终极指南

PingFangSC字体解决方案&#xff1a;跨平台网页字体优化的终极指南 【免费下载链接】PingFangSC PingFangSC字体包文件、苹果平方字体文件&#xff0c;包含ttf和woff2格式 项目地址: https://gitcode.com/gh_mirrors/pi/PingFangSC 还在为网页字体在不同设备上显示效果不…

作者头像 李华
网站建设 2026/4/23 11:34:10

三脚电感在电源管理中的作用:全面讲解

三脚电感为何成为高端电源设计的“隐形冠军”&#xff1f;你有没有遇到过这样的情况&#xff1a;一个DC-DC电路明明按照参考设计来布板&#xff0c;参数也完全匹配&#xff0c;可EMI测试就是过不了&#xff1f;辐射超标几dB&#xff0c;整改起来却要改三四轮PCB&#xff0c;加磁…

作者头像 李华
网站建设 2026/4/23 11:35:16

TikTok视频下载终极指南:免费开源工具全解析

TikTok视频下载终极指南&#xff1a;免费开源工具全解析 【免费下载链接】TikTokDownloader JoeanAmier/TikTokDownloader: 这是一个用于从TikTok下载视频和音频的工具。适合用于需要从TikTok下载视频和音频的场景。特点&#xff1a;易于使用&#xff0c;支持多种下载选项&…

作者头像 李华
网站建设 2026/4/23 11:32:13

Qwen3-VL-WEBUI文本-视觉融合:统一理解部署实战

Qwen3-VL-WEBUI文本-视觉融合&#xff1a;统一理解部署实战 1. 引言&#xff1a;多模态时代的统一理解需求 随着大模型技术的演进&#xff0c;单一模态&#xff08;如纯文本或纯图像&#xff09;已难以满足复杂场景下的智能交互需求。阿里云推出的 Qwen3-VL-WEBUI 正是在这一…

作者头像 李华
网站建设 2026/4/23 11:38:50

Qwen3-VL环境保护:生态监测应用案例

Qwen3-VL环境保护&#xff1a;生态监测应用案例 1. 引言&#xff1a;AI视觉语言模型如何赋能生态保护 随着全球气候变化和生物多样性危机加剧&#xff0c;传统生态监测手段面临人力成本高、覆盖范围有限、响应速度慢等挑战。近年来&#xff0c;多模态大模型技术的突破为环境科…

作者头像 李华
网站建设 2026/4/23 14:49:10

Qwen2.5-7B多模态体验:图文生成10分钟上手,成本透明

Qwen2.5-7B多模态体验&#xff1a;图文生成10分钟上手&#xff0c;成本透明 1. 为什么电商运营需要Qwen2.5-7B&#xff1f; 作为电商运营人员&#xff0c;每天最头疼的事情之一就是撰写海量商品描述。传统方式要么耗费大量人力&#xff0c;要么外包质量不稳定。Qwen2.5-7B这个…

作者头像 李华