存储格式
一个 tantivy 索引是一个 meta.json 加若干个段,一个段是一组共用同一个 uuid 前缀的文件:.term(词典)、.idx(倒排表)、.pos(位置)、.fieldnorm、.store(文档存储)、.fast(快速字段),有删除时再加一个 .{opstamp}.del。段写完就不再修改,唯一会换的是 .del。这一部分按文件逐个讲它们的字节格式、怎么写出来、怎么读,也就是磁盘主线:查询里的一个词从词典查到倒排表,打分时读 fieldnorm,最后从 doc store 取回原文、从快速字段读排序用的值。
代码分布(按 tag 0.27.0 统计 .rs 行数,含单元测试):src/directory/ 3203 行,src/termdict/ 1917 行,src/postings/ 4999 行,src/positions/ 478 行,src/store/ 2813 行,src/fastfield/ 3020 行,src/fieldnorm/ 827 行;快速字段的格式在工作区的 columnar/src/(14438 行),它用到的 sstable/src/ 5388 行、bitpacker/src/ 1875 行。
- Directory:所有段文件都经过
Directorytrait 读写,读出来的是FileSlice而不是Read;默认的MmapDirectory落盘,ManagedDirectory记账并做垃圾回收,每个文件末尾有一个带版本和 crc 的 footer。 - 倒排表:
.idx按词存文档号和词频,每 128 个一块做 bitpacking,带跳表。 - 位置:
.pos存每个词在文档里出现的位置,供短语查询使用。 - 词典:
.term把词映射到它在.idx、.pos里的位置信息,默认用 FST,开quickwitfeature 时换成 sstable。 - 文档存储:
STORED的字段按约 16 KB 一块压缩(默认 lz4),块尾一张偏移表,跳表从文档号找块;每段一个 100 块的 LRU 缓存,在 1289 块的日志段上随机取文档只命中 7.5%。 - 快速字段:
.fast是tantivy-columnar的列存,一个文档几个值按段推断;普通数值列在Bitpacked和BlockwiseLinear之间按估算挑小的,Linear只用于多值列的 start offsets 和 tie-breaker;字符串列存排序字典里的序号。 - fieldnorm 与删除位图:字段长度压成一个字节(0 到 40 精确,之后误差不超过约 11%),BM25 按它查表;删除写成文件名带提交 opstamp 的存活位图,被删文档在合并前仍计入统计,合并时
avgdl也会变。
flowchart TB meta["meta.json"] --> seg["段 uuid"] seg --> term[".term 词典"] term --> idx[".idx 倒排表"] idx --> pos[".pos 位置"] idx --> fnorm[".fieldnorm"] idx --> del[".opstamp.del"] del --> hits["前 10 个文档"] fnorm --> hits hits --> store[".store"] seg --> fast[".fast"] fast --> hits
六种文件各自独立:一个字段可以只进倒排(INDEXED)、只进 doc store(STORED)、只进列存(FAST),也可以都进,schema 怎么组合这些标志见 Schema 与字段。这些文件是段定稿时写出的(doc store 例外,文档一进来就开始写),过程在 段写入;合并时每种文件各有自己的合并逻辑,在 合并。用一次真实提交看一个段的目录清单和 meta.json 原文,见 一个段。