3. 缓存失效机制与触发条件:Refresh、Flush、Merge 操作如何导致缓存失效,以及 Translog 的作用
在 Elasticsearch 中,缓存一致性并非自动保证,而是由一系列底层操作(Refresh、Flush、Merge)的触发条件与副作用共同决定。理解这些操作如何使缓存失效,以及 Translog 如何作为“安全网”保障数据不丢失,是构建可观测性与故障排查能力的基础。
3.1 核心操作概述与缓存失效全景
下表总结了三个关键操作对缓存的影响,以及 Translog 的补偿角色:
| 操作 | 触发条件 | 缓存失效范围 | 数据可见性 | 磁盘写入 |
|---|---|---|---|---|
| Refresh | 默认 1s 间隔 / 手动 POST /_refresh | 文件系统缓存(OS Cache)中的段变为可搜索,但不清空 Filter Cache 或 Field Data Cache | 新数据变为 searchable(近实时) |
仅写入文件系统缓存,未 fsync 到磁盘 |
| Flush | 默认 30min 或 Translog 达到 512MB / 手动 POST /_flush | 清空 Translog,生成新的提交点(commit point),可能触发段合并后的缓存失效 | 数据持久化到磁盘,保证 crash-safe | 执行 fsync,数据落盘 |
| Merge | 后台自动触发(段数量/大小阈值) | 直接导致被合并段的 Filter Cache、Field Data Cache、Request Cache 全部失效 | 合并后段替换旧段,查询路由更新 | 重写段文件,删除旧段 |
可观测性要点: 在监控中,
refresh.time、flush.time 和 merge.time 的突增往往伴随着缓存命中率下降。建议在 Grafana 面板中同时叠加这三条曲线与 fielddata.evictions 指标。
3.2 Refresh 操作:近实时搜索的代价
Refresh 操作将内存缓冲区(Buffer)中的数据写入一个新的段(Segment),并打开该段使其可被搜索。但此过程不保证数据已落盘,仅写入文件系统缓存(Page Cache)。
缓存失效机制
- Filter Cache 不受影响: Refresh 不改变已有段的过滤结果,因此 Filter Cache 中的条目仍然有效。
- Field Data Cache 不受影响: 新段加入后,Field Data 仅在首次对该段执行聚合或排序时才会加载,不会主动驱逐旧缓存。
- Request Cache 部分失效: 如果 Refresh 改变了索引的元数据(如新增字段映射),可能导致部分查询缓存失效,但通常影响较小。
常见误区: 很多运维人员认为 Refresh 会清空缓存,实际上它只负责“让新数据可见”。缓存失效主要发生在 Merge 阶段。频繁 Refresh(如设置
refresh_interval: 100ms)会导致段数量激增,间接加速 Merge 触发,从而间接导致缓存失效。
3.3 Flush 操作:持久化与 Translog 的截断
Flush 操作执行一次完整的提交(commit):将文件系统缓存中的数据 fsync 到磁盘,生成一个新的提交点,并清空 Translog。Flush 本身不直接导致 Filter Cache 或 Field Data Cache 失效,但它会触发以下连锁反应:
- Translog 截断: 清空已提交的 Translog 文件,释放磁盘空间。
- 段文件落盘: 所有未 fsync 的段数据写入磁盘,此时段变为“安全”状态。
- 可能触发 Merge: Flush 后如果段数量超过阈值,Merge 进程会被唤醒,进而导致缓存失效。
Translog 的作用
Translog(事务日志)是 Elasticsearch 保证数据不丢失的核心机制。它记录所有尚未落盘的索引/删除操作,类似于数据库的 WAL(Write-Ahead Log)。
| 特性 | 说明 |
|---|---|
| 故障恢复 | 节点宕机后重启,从最后一个提交点恢复段文件,然后重放 Translog 中未提交的操作,确保数据完整。 |
| 缓存一致性 | Translog 不直接参与缓存管理,但它保证了即使缓存因 Merge 失效,数据也不会丢失。 |
| 性能影响 | Translog 默认每 5s 或每次写操作后 fsync(取决于 index.translog.durability 设置),是写入延迟的主要来源之一。 |
故障排查线索: 如果发现节点重启后缓存预热时间异常长,应检查 Translog 重放耗时。在日志中搜索
recovery completed 和 translog recovery 关键字,结合 indices.recovery API 获取详细进度。
3.4 Merge 操作:缓存失效的“主犯”
Merge 操作将多个小段合并为一个大段,以减少段数量、提升查询性能。但合并过程直接且彻底地导致缓存失效,是缓存一致性问题的最大来源。
缓存失效的详细过程
- 段被标记为删除: 被合并的旧段不再用于查询,但仍在内存中。
- 新段创建并打开: 新段替换旧段,查询路由切换到新段。
- 缓存条目失效:
- Filter Cache: 所有基于旧段的位集(bitset)被逐出。
- Field Data Cache: 所有基于旧段的字段值被逐出,新段需要重新加载。
- Request Cache: 如果查询涉及被合并的段,缓存结果被标记为无效。
- 缓存预热: 后续查询触发缓存重新加载,导致查询延迟突增。
可观测性指标: 使用
GET /_nodes/stats/indices/merge 查看当前合并线程数、合并文档数、合并时间。当 merge.current 持续大于 0 且 fielddata.evictions 同步上升时,说明缓存正在被 Merge 大量驱逐。
3.5 缓存失效链路与 Translog 的补偿角色(SVG 图)
下图展示了从数据写入到缓存失效的完整链路,以及 Translog 如何作为安全网保障数据不丢失:
3.6 缓存失效的触发条件总结
以下列表归纳了各操作触发缓存失效的具体条件,便于在故障排查时快速定位:
- Refresh 触发缓存失效的条件:
- 几乎不直接触发缓存失效,但频繁 Refresh 会导致段数量增长,间接加速 Merge。
- 如果 Refresh 伴随映射更新,可能导致 Request Cache 部分失效。
- Flush 触发缓存失效的条件:
- 不直接失效缓存,但 Flush 后如果段数量超过
index.merge.policy.segments_per_tier阈值,会触发 Merge。 - Flush 清空 Translog,释放内存,但不会影响 Filter/Field Data Cache。
- 不直接失效缓存,但 Flush 后如果段数量超过
- Merge 触发缓存失效的条件:
- 任何 Merge 操作都会导致被合并段的缓存失效。
- 大段合并(如
max_merged_segment达到 5GB)会导致大量缓存被逐出。 - 强制合并(Force Merge)会一次性失效所有相关段的缓存,影响最大。
生产环境建议: 在写入高峰期,应避免执行 Force Merge 或手动 Flush,以免引发大规模缓存失效导致查询延迟飙升。建议将
index.merge.scheduler.max_thread_count 限制为 Math.min(4, Runtime.getRuntime().availableProcessors() / 2),降低 Merge 对缓存的冲击。
3.7 可观测性视角:如何监控缓存失效
在链路追踪与故障排查中,以下指标是判断缓存失效是否由 Refresh/Flush/Merge 引起的直接依据:
| 指标 | API 路径 | 关联操作 | 异常阈值参考 |
|---|---|---|---|
fielddata.evictions |
GET /_nodes/stats/indices/fielddata |
Merge | 持续 > 10/s 需关注 |
filter_cache.evictions |
GET /_nodes/stats/indices/filter_cache |
Merge | 持续 > 50/s 需告警 |
merge.total_time_in_millis |
GET /_nodes/stats/indices/merge |
Merge | 单次 Merge > 10s 需分析 |
refresh.total_time_in_millis |
GET /_nodes/stats/indices/refresh |
Refresh | 单次 Refresh > 1s 需优化 |
translog.size_in_bytes |
GET /_nodes/stats/indices/translog |
Flush | 超过 1GB 需检查 Flush 频率 |
链路追踪实践: 在 APM 工具中,将
search_query_latency 与 merge.current 绘制在同一时间轴。如果查询延迟峰值与 Merge 活动窗口高度重合,即可确认缓存失效是延迟突增的根因。此时应优先调整 Merge 策略(如增大 index.merge.policy.max_merged_segment)而非盲目扩容。