当前位置: 首页 > news >正文

DocumentsWriterDeleteQueue

Lucene 核心索引模块中的DocumentsWriterDeleteQueue类它是 Lucene 实现高并发、强一致性删除delete和更新update语义的关键基础设施。我们可以用一句话概括它的作用✅DocumentsWriterDeleteQueue是一个全局的、非阻塞的、单向链表结构的“待处理删除操作队列”用于在多线程写入add/update/delete场景下保证所有删除操作按提交顺序被正确应用到每个即将 flush 的 segment 上。 一、为什么需要它——问题背景在 Lucene 中多个线程可以同时调用addDocument()、updateDocument()、deleteDocuments()每个线程有自己的DocumentsWriterPerThreadDWPT独立缓冲文档但删除操作是全局的一个delete(term)应该影响所有 segment包括未来 flush 出来的挑战如何让每个 DWPT 在 flush 时知道“截至此刻哪些 delete 已经发生”并且要保证先 add 后 delete → 文档不出现先 delete 后 add → 文档出现这就需要一个全局有序的 delete 日志而DocumentsWriterDeleteQueue就是这个日志。 二、核心设计链表 DeleteSlice1.全局链表结构private volatile Node? tail;所有 delete 操作term/query/doc-values update都被封装为Node追加到链表尾部链表是单向、无头只有 tail、带哨兵节点sentinel追加操作是synchronized的保证严格顺序2.每个消费者持有一个 DeleteSliceclass DeleteSlice { Node? sliceHead; // 上次处理到的位置不包含 Node? sliceTail; // 本次需要处理到的位置包含 }每个 DWPT有自己的DeleteSlice全局 delete pool也有一个globalSliceslice表示“我需要处理从 head 到 tail 之间的 delete” 这种设计让 GC 自动回收已处理的节点只要没有 slice 引用它⚙️ 三、关键方法解析方法作用add(Node, DeleteSlice)用于updateDocument(doc, delTerm)1. 将 delTerm 加入全局队列2.原子性地更新调用者的 slice.tail 新节点→ 保证该 delete 会被本 DWPT 在 flush 时应用freezeGlobalBuffer(DeleteSlice callerSlice)DWPT flush 前调用1. 锁住全局 buffer2. 将 callerSlice.tail 推进到当前 tail3. 返回一个FrozenBufferedUpdates快照包含所有 delete→ 用于构建FlushedSegmenttryApplyGlobalSlice()异步尝试将新 delete 应用到globalBufferedUpdates供后续 merge 或 searcher 使用newSlice()为新 DWPT 创建初始 slice指向当前 tail 四、典型流程updateDocument(doc, delTerm)这是最能体现其价值的场景indexWriter.updateDocument(new Term(id, 123), doc);内部步骤DWPT-A 处理 doc调用deleteQueue.add(delTermNode, dwptSlice)全局队列追加delTermNodedwptSlice.sliceTail delTermNode← 关键DWPT-A flush 时调用freezeGlobalBuffer(dwptSlice)将 slice.tail 推进到最新 tail可能包含其他 deletedwptSlice.apply(...)→ 应用所有 delete包括自己的 delTerm如果 doc 匹配delTerm→ 不写入 segment✅ 即使多个线程同时 update 同一个 term也能保证只有一个成功写入因为 slice.tail 原子更新 五、全局 vs 局部视角视角说明全局队列所有 delete 的“事实来源”严格有序DWPT 的 slice“我关心哪些 delete” —— 从上次 flush 到现在globalSlice“哪些 delete 还没应用到已提交的 segments”→ 用于生成.del文件或 merge 时应用 六、类比理解想象一个多人协作编辑的 Google Docs每个人DWPT在自己的草稿区写内容所有“删除某段文字”的操作delete被记录在一个共享操作日志DeleteQueue中当某人要“提交草稿”flush时他查看日志“从我上次提交后有哪些删除”把这些删除应用到自己的草稿上提交最终版本✅DeleteSlice就是每个人的“日志阅读进度条”✅ 七、总结DocumentsWriterDeleteQueue的核心价值全局顺序保证所有 delete 严格按调用顺序执行跨 DWPT 一致性任何 delete 对所有 segment 可见高效并发无锁读slice 是线程局部仅写入加锁内存安全GC 自动回收已处理节点支持 update 语义通过add(node, slice)实现原子性它是 Lucene 实现“实时更新”先删后加和“高吞吐写入”的基石之一。这个“上次提交”在 Lucene 的上下文中并不是指 segment 已经写入磁盘并对外可见而是指✅“当前 DWPT 上一次完成 flush 准备即调用freezeGlobalBuffer并重置 DeleteSlice的那个时间点”换句话说“上次提交” “上次 flush 时我处理 delete 的截止位置”。 一、为什么不是“真正提交到索引”因为DWPT 的 flush 是异步的flush 完成后segment 还要经过 publish 才对 searcher 可见DeleteSlice 是 DWPT 私有的状态只关心“我自己缓冲的文档需要应用哪些 delete”所以这里的“提交”是DWPT 内部视角的“逻辑提交点”不是全局索引的提交。 二、具体解释“上次提交”到底是什么每个 DWPT 持有一个DeleteSlice它有两个指针Node? sliceHead; // ← “上次提交”的位置即上次 flush 时处理到的 tail Node? sliceTail; // ← 当前需要处理到的位置生命周期示例时间操作全局队列DWPT-A 的 DeleteSliceT0DWPT-A 创建[sentinel]head tail sentinelT1Thread-1:delete(term1)[sentinel → term1]不变T2Thread-2:add(doc1)→ DWPT-A同上不变T3DWPT-A flush同上调用freezeGlobalBuffer()-sliceTail推进到term1- 应用term1-reset()→head tail term1T4Thread-3:delete(term2)[... → term2]不变T5Thread-4:add(doc2)→ DWPT-A同上不变T6DWPT-A 再次 flush同上freezeGlobalBuffer()- 发现sliceTail (term1) ! globalTail (term2)- 推进 tail 到term2- 应用term2✅ 所以“上次提交” 上一次 flush 时reset()后的sliceHead等于当时的sliceTail⚙️ 三、代码中的体现在DeleteSlice.reset()中void reset() { sliceHead sliceTail; // ← 关键将 head 移到当前 tail }而reset()被调用的地方是// 在 freezeGlobalBuffer - apply 之后 deleteSlice.apply(...); deleteSlice.reset(); // ← 标记“本次 flush 已处理到此处”所以sliceHead始终指向“上次 flush 时处理完的最后一个 delete 节点”下次 flush 时从head.next开始处理直到新的tail️ 四、图解“上次提交”的含义全局删除队列: [sentinel] → [del1] → [del2] → [del3] → [del4] ↑ ↑ sliceHead (上次提交) sliceTail (本次要处理到)DWPT 在 T3 flush 时处理了 del1、del2 → reset 后head del2现在 T6 flush发现新来了 del3、del4 → 处理它们处理完后 reset →head del4✅ “上次提交” sliceHead所指的位置❓ 五、为什么这样设计避免重复处理如果不记录head每次 flush 都从头开始遍历 delete 队列 → 性能灾难保证恰好一次语义每个 delete 节点被每个 DWPT恰好处理一次支持并发每个 DWPT 独立维护自己的进度互不影响GC 友好一旦所有 DWPT 的sliceHead都越过了某个节点该节点就不可达 → 被回收✅ 六、总结回答“上次提交”指的是当前 DWPT 上一次 flush 时通过DeleteSlice.reset()记录的删除处理截止位置即当时的sliceTail。它是DWPT 私有的逻辑进度标记用于确保不遗漏新 delete不重复处理旧 delete正确实现 update/delete 的语义顺序这和数据库中的“LSNLog Sequence Number”或“游标cursor”概念非常相似——是一种增量同步机制。
http://www.zskr.cn/news/1384966.html

相关文章:

  • 美业门店拓客模式开发介绍
  • Rust Slice(切片)类型
  • 2026年了,GEO生成引擎优化到底在优化什么?一文讲透底层逻辑与实战框架
  • 西恩士-航空螺栓螺母紧固件表面油污清洁度分析设备 - 工业干货社
  • 【DeepSeek生产部署生死线】:从吞吐跌57%到SLO达标99.99%,6个被官方文档忽略的关键配置
  • DeepSeek服务稳定性生死线:如何用3步熔断策略+5级降级开关扛住10倍流量洪峰?
  • 广州因特智能:AI视觉软硬结合,打破半导体检测装备“卡脖子”困境
  • 毕业设计:基于mvc的高校办公室行政事务管理系统设计与实现(源码)
  • 2026长沙智能家居品牌实测,这些本地老牌值得选
  • 翻译 GDB 官方文档
  • 网络配置工具类详解
  • 黑客必刷的 23 个网安攻防靶场,零基础到红队全覆盖
  • Hotkey Detective终极指南:彻底解决Windows热键冲突的免费神器
  • 告别抓瞎!手把手教你用Postman搞定微信小程序接口测试(附环境变量与断言实战)
  • MCBx51评估板:8051单片机开发全兼容方案解析
  • 举一个具体例子说明为什么索引不是越多越好,举具体字段
  • CanTSyn vs PTP:在ADAS域控制器里,我们最终为什么选了它?
  • 告别CAJ格式困扰:3分钟学会用开源工具将知网文献转为PDF
  • 一个取巧但有效的方法:利用PAT报错信息反向“猜”出测试数据(附Python二分脚本)
  • 从“DOC/PDF”到“WPS”:细看GJB438C-2021文档格式要求背后的国产化信号与落地指南
  • OBS多平台直播革命:obs-multi-rtmp插件让你一次推流,全网覆盖
  • 深挖 okbiye 核心能力|AI 毕业论文写作新模式,高效攻克毕业创作难题
  • Redis持久化机制详解
  • 百度文心一言开发者如何通过Taotoken低成本接入多模型API
  • 2026 年 AI 毕业论文工具横评:从降 AIGC 率到智能排版,10 款平台实测谁才是毕业季的 “救命稻草”
  • 2027考研全套资料免费分享
  • DragonBones与Godot集成:骨骼动画的可编程化实践
  • 2026 上海 GEO 优化机构实力榜:AI 搜索第一推荐位抢占攻略 - GEO优化
  • 从游戏引擎到仿真平台:手把手教你用AirSim+UE4搭建你的第一个无人机/自动驾驶仿真环境
  • Python UiAutomation实战:从网页数据抓取到桌面应用,一个库打通数据采集全链路