写入
写入主线(加三篇文档,提交一次)从 Index::writer 开始,到 meta.json 换成新版本、后台合并结束为止。代码集中在 src/indexer/(0.27.0 下 11518 行,含单元测试),倒排表的内存部分在 src/postings/ 和 stacker crate,0.27.0 起一个段由哪几个文件组成、各自怎么写,改由 src/index/inverted_index_plugin.rs、src/fastfield/plugin.rs、src/store/plugin.rs 这几个「内置插件」负责。
这一部分回答四个问题:一个 writer 开几个线程、每个线程能用多少内存、为什么同一个目录只能有一个 writer;一篇文档进来之后写到哪些内存结构、段在什么时候算满;commit 到底做了哪几步,删除怎么落到各个段,读者什么时候能看到;段越来越多以后谁来合并,删除的文档什么时候真正消失。
- IndexWriter:线程数是
min(核数, 8),每线程不足 15 MB 时按总预算 / 15 MB减线程,本机writer(50_000_000)开 3 个索引线程、每个 16,666,666 字节;目录锁是flock,第二个 writer 拿到LockBusy;drop 一个没提交的 writer 会先把队列里最多 10,000 篇文档索引完再扔掉。 - SegmentWriter 与内存池:0.27.0 的
SegmentWriter把文档交给倒排、快速字段、doc store 三个插件写手;倒排表放在stacker的ArenaHashMap和MemoryArena里;一个空的段写手就报 13,634,680 字节,15 MB 预算的线程每段只装得下约 1,260 篇维基摘要。 - 提交:
prepare_commit切断 channel、等线程交段,SegmentUpdater线程依次落删除、原子写meta.json、GC、考虑合并;加三篇文档提交一次,commit()返回 4 而不是 2;commit_opstamp()从 0.4.3 起就不再更新;本机一次单文档提交约 60 ms,几乎全是落盘同步。 - 合并:
LogMergePolicy把 1 万篇以下的段都归到同一层,凑够 8 个才合并;删除的文档只在段被重写时消失,默认的删除比例阈值 1.0 等于不看删除;writer 一 drop,正在跑的合并结果就被丢掉,每次新开 writer、提交完就 drop 的用法下段数只增不减;0.27.0 恢复了索引排序。
flowchart TB w["Index writer"] --> add["add_document"] add --> ch["有界 channel"] ch --> sw["SegmentWriter"] sw -->|内存满| fin["finalize 写段文件"] sw -->|commit| fin fin --> su["SegmentUpdater"] su --> del["落删除 写 del"] del --> meta["原子写 meta.json"] meta --> gc["GC"] gc --> mp["LogMergePolicy"] mp --> mg["合并线程"] mg --> su
段文件在磁盘上的格式(.idx、.term、.store、.fast、.fieldnorm、.del)是存储格式那一部分的事;提交以后读者怎么重新加载,在IndexReader 与 Searcher。整条写入主线的时序图在一次写入。本站的搜索模块每次 upsert 都新开一个 writer,这一部分的几个实验专门复现了这种用法,结论在合并和在本站中。
💬 关联讨论
发起讨论加载中...