先把当时的情况摆出来。FAISS 的 IndexFlatIP 搬到 GPU 上建了 500 万条,显存 22GB 不够再扩,转回 CPU 继续 add 时返回的 id 从 0 重新开始,新向量直接覆盖了旧 id。

真正麻烦的地方在于,几个原因的现象几乎一样。迁移时只换了索引对象,没有复制 id 映射。另一种情况是外层包了 IDMap 而内层索引被整体替换。

不容易想到的是,CPU 索引不支持传入的 id 类型。还有一种情况,重建索引时没把旧 id 一起写回。

上来先确认索引类型与总条数

python -c "import faiss;idx=faiss.read_index('x.faiss');print(type(idx), idx.ntotal)"

随后用 IDMap2 包装后再迁移

python -c "import faiss;idx=faiss.read_index('x.faiss');idx=faiss.IndexIDMap2(idx);print(idx.ntotal)"

迁移前后比对首尾 id

python faiss_ids.py --file x.faiss --show-head-tail

等前面都确认过,再用 add_with_ids 批量补写并校验

python faiss_add_ids.py --start 5000000 --n 1000

中间停过一次,把已经改的东西回滚干净,再重新来一遍。

同样的操作我在测试机上先走了一遍,线上只做验证过的部分。

别只看报错消失,验证要多做一步。迁移后总条数不变,随机抽查询返回的 id 与迁移前一致,覆盖现象消失。

FAISS 的 id 是索引结构的一部分,换引擎时先包 IDMap2,能避免大部分错位。这类问题最好一次改到位,反复小改反而容易把原因搅混。

作者 9823189