FAISS 索引读写与合并
FAISS 的索引可以落盘复用:write_index 写出文件、read_index 读回,省去每次服务的重建开销;配合 IndexIDMap 与 add_with_ids 能把业务主键直接存进索引,检索返回的就是业务 id。分片构建的索引还能用 merge_from 合并,适合「先分片离线构建,再汇总成一份」的流水线。前提条件是同类型、同维度、同度量的索引才能合并,业务 id 需要 int64。
基本信息
| 持久化 | write_index 写出、read_index 读回 |
|---|---|
| 业务主键 | IndexIDMap 配合 add_with_ids |
| 合并 | merge_from,要求同类型同维度同度量 |
| id 类型 | 必须为 int64 |
| 常用结构 | IndexFlatIP 精确检索,另可选用 IVF 与 HNSW |
| 典型用途 | 离线批量构建、分片汇总、服务启动加载 |
典型使用场景
- 离线流水线分片构建索引,最后合并成一份完整索引
- 服务启动时加载预构建索引,避免每次重建
- 用业务主键检索,省掉序号到 id 的映射表
- 把线上索引定期落盘,用于快速回滚
- 多个数据源各自构建后合并,避免一次性占用大内存
IndexIDMap 与合并
IndexIDMap 与合并
import faiss, numpy as np
idx = faiss.IndexFlatIP(384) # 内积精确索引
idmap = faiss.IndexIDMap(idx) # 包装成可带业务 id
ids = np.arange(1000, 1010, dtype="int64") # 业务 id 必须 int64
idmap.add_with_ids(np.random.rand(10, 384).astype("float32"), ids)
faiss.write_index(idmap, "part.faiss") # 落盘
p2 = faiss.read_index("part.faiss") # 读回
p1 = faiss.read_index("part_a.faiss")
p1.merge_from(p2) # 同类型索引可合并
faiss.write_index(p1, "merged.faiss")接口读法
| 接口 | 作用 | 注意 |
|---|---|---|
| write_index 与 read_index | 索引持久化 | 文件格式与 FAISS 版本相关,跨版本加载需先验证 |
| add_with_ids | 写入并指定业务 id | id 必须为 int64 |
| merge_from | 合并两份索引 | 要求同类型、同维度、同度量 |
| IndexFlatIP | 精确内积检索 | 向量需先归一化,内积才等价于余弦 |
用 add_with_ids 后检索直接返回业务 id,可以省掉额外维护的序号映射表,也避免了映射错位导致的取错文档。
read_index 加载的索引文件与构建它的 FAISS 版本绑定:跨版本升级时先用小样本验证能否加载,避免线上启动即失败。
IndexFlatIP 属于精确检索:数据量到百万级时内存与延迟都会明显上升,此时应换成 IVF 或 HNSW 近似索引,并用同一评测集确认召回损失可接受。
合并前先校验分片之间有没有重复的业务 id:不同分片写入相同 id 会得到重复命中,排序时同一个文档占满多个返回位。