先把当时的情况摆出来。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,能避免大部分错位。这类问题最好一次改到位,反复小改反而容易把原因搅混。
