问题出现得很突然,日志倒是留得挺全。本地用 faiss 1.8 的 IndexIDMap2 包了一层 IVF4096,写入时传业务文档 id,重启后 read_index 读回来,search 返回的 id 全变成 0、1、2 这样的序号,和数据库对不上,清理脚本按返回 id 删错了两条记录,索引文件 2.4GB。
刚开始盯着「重建时用 IndexFlat 顶替,原来的映射被整个丢掉」,折腾了半个多小时,排掉了。
把日志和配置对完,能解释这个现象的基本就这几处。写入时调的是 add 而不是 add_with_ids,映射表里没有业务 id。少数情况下是只序列化了外层 IndexIDMap2,内层 quantizer 的 id 映射没落盘。
少数情况下是业务 id 超过 int32 范围,在转换时被截断成序号。
第一件事是加载后先打印索引类型和 ntotal
python -c "import faiss;i=faiss.read_index('docs.index');print(type(i).__name__, i.ntotal, i.id_map.size())"
然后抽三条看返回的 id 是不是业务 id
python -c "import faiss,numpy as np;i=faiss.read_index('docs.index');print(i.search(np.zeros((1,1024),dtype='float32'),3)[1])"
接着改成 add_with_ids 重建并重新落盘
python rebuild_index.py --input docs.npy --ids ids.npy --out docs.index
再读一次比对前五个 id 与源文件
python -c "import faiss,numpy as np;i=faiss.read_index('docs.index');print(i.search(np.load('q.npy'),5)[1]);print(np.load('ids.npy')[:5])"
改完先别撤,确认这几项。read_index 之后 id_map.size 与写入条数一致,search 返回的 id 能在业务表里查到对应文档,删除脚本按 id 操作不再误删。
IndexIDMap2 的映射是独立结构,必须和索引一起序列化;如果一开始就用了 add,后面再怎么 read/write 都补不回业务 id,只能重建。报错文案容易误导,真正的原因往往在相邻那一层的日志里。
