土法炼钢兴趣小组的算法知识备份

【全文检索引擎】refresh · flush · translog:近实时与持久化边界

文章导航

分类入口
databasestorage
标签入口
#elasticsearch#lucene#nrt#refresh#flush#translog#durability#wait-for#refresh-interval#8.x

目录

运维常把「写进 ES 了」等同于「立刻能搜到」或「已经落盘不怕宕机」——这是三件事。refresh 打开新的 Lucene Searcher,决定 近实时(NRT)可搜flush 触发 Lucene commit(),把段 持久化到磁盘 并裁剪 translog;translog 记录尚未安全提交的索引操作,支撑 节点崩溃后的重放。三者正交,混谈会导致错误的 SLA 与错误的 refresh_interval 调参。

第 7 篇单机 Lucene 上讲了 IndexWriter 缓冲 flush 成段、DirectoryReader.open(writer) 的 NRT 读者;第 11 篇 把段模型抬到 分片 + 副本复制。本篇锚定 Elasticsearch 8.x ReferenceNear real-time searchTranslogIndex modules),给出 保证矩阵,说明 refresh_interval?refresh=wait_for 的语义差异,并用本机 ES 8.15.0 单节点 做一次可见性对照实验。

本文是「全文检索引擎」系列第 12 篇(共 18 篇)。→ 系列目录

版本锚定:Elasticsearch 8.15.0(Docker 单节点);Lucene 9.11.1。实验在 WSL2 上执行,下文输出经删减仅保留命中数。


一、写入路径:内存缓冲、translog 与复制

单分片上的索引操作(index / bulk)在数据节点上大致经过:

  1. 解析与映射(见 第 14 篇)。
  2. 写入 内存 indexing buffer,同时 append translog(每个分片独立 translog 文件)。
  3. index.translog.durability 决定是否 fsync translogrequest 每次写入后;asyncsync_interval 批量)。
  4. 若该分片有副本,主分片把操作 复制 到副本分片(副本也有 translog)。
flowchart LR
  W["Write request"]
  IB["Indexing buffer<br/>in-memory segments"]
  TL["Translog<br/>append + optional fsync"]
  RF["refresh<br/>new Searcher"]
  FL["flush<br/>Lucene commit"]
  W --> IB
  W --> TL
  IB --> RF
  IB --> FL
  FL --> TL2["trim translog"]

关键点:在 refresh 之前,数据已在内存缓冲与 translog 中,但 默认 Searcher 看不到 新段;在 flush 之前,新段可能只在 OS page cache 或纯内存路径上,Lucene commit 点 未前进。


二、保证矩阵:可搜、落盘、可恢复

机制 对搜索可见 对 Lucene commit / 段文件 崩溃后未提交段 典型触发
仅写入 buffer 无新 commit 靠 translog 重放 每次 index
refresh (新 Searcher) 不一定 fsync 段文件 仍靠 translog 定时 / 手动 _refresh
flush commit + 段持久化 translog 可裁剪 translog 大小 / 定时 / _flush
translog fsync 不影响可见性 不代替 commit 重放索引操作 durability=request

表意对齐 Reference Near real-time searchTranslogrefresh 解决「读路径」flush 解决「Lucene 持久化点」translog 解决「commit 之间的操作日志」

与关系库对照:InnoDB redo log + doublewrite 也区分 脏页刷盘日志重放;ES 把 可搜 提前到 refresh,换取默认约 1s 的 NRT 窗口(architecture/42 NRT 节 有场景化描述,本篇钉语义)。

2.1 refresh 做了什么

refresh 在分片上等价于:把内存缓冲 形成新 Segment(或暴露已有段)并 打开新的 IndexSearcher 供搜索线程使用。默认 index.refresh_interval1s,即周期性 refresh。refresh 替代 flush:未 flush 的段在进程崩溃时仍可能丢失,除非 translog 重放后重新索引。

2.2 flush 做了什么

flush 调用 Lucene IndexWriter.commit():把待提交段 固化到磁盘 并写入 commit 点(segments_N)。成功后 ES 可 截断或删除 已覆盖的 translog 区间。index.translog.flush_threshold_size(默认约 512mb)与 index.translog.sync_interval 等共同决定 flush 频率。

2.3 translog 与 durability

index.translog.durability


三、近实时 vs 持久化 vs 崩溃恢复

用时间线理解三者 不互相替代

sequenceDiagram
  participant Client
  participant Primary as Primary shard
  participant Disk as Disk / translog
  Client->>Primary: index (refresh=false)
  Primary->>Disk: append translog
  Note over Primary: not searchable yet
  Primary->>Primary: refresh
  Note over Primary: searchable (NRT)
  Primary->>Disk: flush / commit
  Note over Disk: segment durable; translog trim
读者关心 应看的 knob 不应指望
写入后多久能搜到 refresh_interval?refresh=wait_for flush 频率
单机宕机不丢已 ack 写 translog.durability=request、副本 wait_for_active_shards refresh
磁盘上段文件完整 flush / ILM forcemerge 边界 仅 refresh
跨节点同时可见 复制 + refresh 传播 单分片 translog

强一致搜索在 ES 中并非默认模型:即使 refresh=wait_for,保证的是 参与刷新的分片已提交索引操作 对搜索可见,而非关系库意义的 线性一致读(争论见 Lamport 风格可用性取舍;工程上 ES 选择 NRT + 复制 服务搜索 SLA)。


四、refresh_interval、请求级 refresh 与 wait_for

4.1 索引级 refresh_interval

PUT /logs-2026.07.15
{
  "settings": {
    "refresh_interval": "30s",
    "number_of_shards": 1,
    "number_of_replicas": 0
  }
}

4.2 请求级 refresh 参数

POST /idx/_doc?refresh=true:写后 立即 refresh 该分片相关范围(代价高,不适合高 QPS 流写)。refresh=false(默认)依赖周期 refresh。

refresh=wait_for:写线程阻塞直到 下一次 refresh 使本写入在搜索中可见(或超时)。适合 写后立刻读 的集成测试与低 QPS 业务;高并发下会把 refresh 压力集中到协调路径。

4.3 本机实验:默认 1s vs refresh_interval: -1

环境:Docker elasticsearch:8.15.0,单节点,number_of_replicas: 0,WSL2 Linux。

# 索引 A:默认 refresh_interval(1s)
curl -s -X PUT 'http://127.0.0.1:19200/refresh_demo_default' \
  -H 'Content-Type: application/json' \
  -d '{"settings":{"number_of_shards":1,"number_of_replicas":0}}'

# 索引 B:禁用自动 refresh
curl -s -X PUT 'http://127.0.0.1:19200/refresh_demo_manual' \
  -H 'Content-Type: application/json' \
  -d '{"settings":{"number_of_shards":1,"number_of_replicas":0,"refresh_interval":"-1"}}'

DOC='{"title":"nrt visibility probe"}'
curl -s -X POST 'http://127.0.0.1:19200/refresh_demo_default/_doc?refresh=false' \
  -H 'Content-Type: application/json' -d "$DOC"
curl -s -X POST 'http://127.0.0.1:19200/refresh_demo_manual/_doc?refresh=false' \
  -H 'Content-Type: application/json' -d "$DOC"

写入后立即搜索(未手动 refresh):

default hits: 0
manual hits: 0

对 manual 索引执行 POST /refresh_demo_manual/_refresh 后再搜:manual hits after refresh: 1

等待 2s 后对 default 索引搜索:default hits after 2s: 1

结论与 Reference 一致:不 refresh 则不可搜;默认 1s 周期下 亚秒级延迟内 可见;-1 必须显式 refresh 或接受不可搜窗口。本实验 测量 flush/translog 崩溃恢复——该路径需受控 kill 节点,留给 reproduce/ 脚本(不编造恢复耗时)。


五、主副本与 refresh 的可见性

副本分片 复制 indexing 操作 后,各自执行 refresh。搜索命中副本时,可见性取决于 该副本是否已 refresh?refresh=wait_for 等待的是 所有参与刷新的活跃分片(含副本策略)达到一致可见点——具体等待集合以 8.x Reference Refresh 为准。

写入一致性常用:

PUT /idx/_settings
{
  "index.write.wait_for_active_shards": "1"
}

all 要求全部副本活跃才 ack——复制耐久搜索可见 仍由 refresh/translog 分别保证,勿混为一谈。


六、学术谱系、争论与开放问题

谱系:Lucene NRT(DirectoryReader.open(IndexWriter))→ Solr/ES 把 refresh 服务化 → translog 作为 commit 间 WAL(与 LSM WAL 类比见 RocksDB WAL)→ 日志栈加大 refresh_interval 换吞吐(observability/08)。

争论

  1. NRT 默认 1s 是否「够实时」:流式告警要毫秒级时常旁路 Kafka + 流处理;ES 优化的是 搜索索引 而非 事件总线architecture/42 场景分工)。
  2. async translog 是否「可接受丢数据」:可接受性取决于业务 是否可重放(日志)vs 不可重放(订单);不是引擎自动判断。

开放问题


七、常见误解

误解 事实
「写入成功 = 立即可搜」 默认最多约 1s refresh 窗口;refresh=false 时更久
「refresh = 落盘」 refresh 只开 Searcher;flush 才 Lucene commit
「flush 越勤越安全」 过频 flush 产生 小段、放大 merge 与 第 8 篇 代价
「关掉 translog 能提速」 translog 是崩溃恢复与复制重放的 必要 WAL(除只读挂载等特殊场景)
wait_for = 强一致」 只保证 搜索可见点;不替代跨文档事务
「副本不需要 refresh」 每个分片独立 Searcher;副本也要 refresh 才能被搜到

八、小结

三句话小结

  1. refresh 决定 NRT 可搜flush 决定 Lucene commit 与段持久化translog 在 commit 之间提供 崩溃重放,三者应用保证矩阵分开调参。
  2. refresh_interval=-1 与默认 1s 的可见性差异已在 ES 8.15 单节点 验证:不 refresh 命中为 0,显式 refresh 或等待周期后才为 1。
  3. 写后读用 wait_forrefresh=true 换延迟;高吞吐灌库用 -1 + 批量结束再 refresh,并单独评估 translog.durability

下一篇沿 协调节点 拆解 scatter-gather 的 query 与 fetch 两阶段:查询路径


参考资料

规范(A 级)

  1. Elasticsearch 8.x Reference, Near real-time search
  2. Elasticsearch 8.x Reference, Translogindex.translog.durability
  3. Elasticsearch 8.x Reference, Index modulesrefresh_intervalflush 相关设置。
  4. Elasticsearch 8.x Reference, Document APIsrefresh 查询参数、wait_for
  5. Apache Lucene 9.x, Near-real-time search(与 第 7 篇 一致)。

实验(A 级)

  1. 本机 Docker elasticsearch:8.15.0refresh_demo_default / refresh_demo_manual 可见性对照(2026-07-15,WSL2)。

站内对读(B 级)

  1. 搜索引擎架构(NRT 场景与 async translog 取舍)。
  2. 本系列 第 7 篇第 11 篇

返回 系列目录 | 上一篇:Index · Shard · Replica | 下一篇:查询路径

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-07-15 · database / storage

【全文检索引擎】Lucene · BM25 · Segment · Elasticsearch NRT

补齐搜索架构叙事与 RAG/向量引擎之间的全文检索内核层:以 Lucene 9.x/10.x 拆解 Analyzer、FST、postings、BM25 与 IndexWriter,并以 Elasticsearch 8.x 拆解分片、refresh/translog 与查询路径;OpenSearch 对照与选型收束。


By .