向量库容量测算台

选索引前先算容量:HNSW、IVF 与暴力检索的内存账差在哪

同一批向量在 flat、HNSW Flat 和 IVF Flat 中的向量本体都是 4 × d 字节。容量差别不在裸向量,而在索引附加结构:HNSW 要存图连接,IVF 要存入倒排项的向量 ID,IVF-PQ 与标量量化则会改变向量本身的存在形式。

因此,容量账不能把所有索引都当成同一种存储。下面只采用 Faiss 官方 wiki 给出的字节公式,并把 HNSW 原论文与 pgvector 官方仓库用于解释结构边界。所有结果都只是下限估算,未计入的实现与存储占用只会让真实占用更高。

先把裸向量摘出来:4 × d

设向量维度为 d,条数为 N。完整保存全部向量时,向量载荷为:

N × 4 × d

这是最先摘出来的公共账。4 × d 不包含图连接、倒排 ID、量化码本或索引实现的其他存储。

对于仍然保存完整向量的 flat、HNSW Flat 和 IVF Flat,这部分完全相同。真正需要单独核算的,是各自附加的结构。

如果使用 IVF-PQ、IVF-SQ 等压缩形式,就不能再把 N × 4 × d 无条件加到最终结果上。此时索引保存的已经是量化后的表示,应直接采用对应的官方每条字节公式。

flat:暴力检索的向量账就是 4 × d

Faiss 官方 wiki 把 IndexFlatL2 与 IndexFlatIP 都列为精确穷举检索,Bytes/vector 均为:

4 × d

flat 不再建立邻接图,也不把向量拆进倒排表。它所做的容量工作,就是保存用于距离计算的完整向量。

按整批向量计算,向量载荷下限为:

N × 4 × d

这个公式在向量载荷层面是纯算术:给定 d,每条字节数可以精确到字节;给定 N,整批向量载荷也可以直接算出。它不表示进程、文件或数据库运行时的完整占用。

IVF:多出来的 8 字节是向量 ID

IndexIVFFlat 先用另一个索引把向量分配到倒排表。它仍保存完整向量,所以向量本体仍是:

4 × d

倒排项还要能够指回原向量。Faiss 官方 wiki 对额外 8 字节的解释是:

向量 ID 需要被存储。

因此,IVF Flat 的每条字节公式是:

4 × d + 8

这里的 8 是向量 ID,不是倒排表地址,也不是粗量化中心的存储大小。倒排表把向量组织到所属列表后,仍要保留原向量的身份,才能从倒排项定位到对应向量。

按整批条数计算,向量与 ID 的载荷下限为:

N × (4 × d + 8)

给定 d 与 N,这个公式可以直接做纯算术。nlists 会改变倒排组织,但官方这条每条向量公式没有给出由 nlists 推导额外倍数的规则。

IVF-PQ:ceil(M × nbits / 8) + 8 从哪里来

IndexIVFPQ 使用粗量化器分配倒排列表,并对残差做乘积量化。乘积量化会把向量拆成由 M 表示的多个子向量,每个子向量按 nbits 量化。

每个向量的编码因此包含:

M × nbits 位

换算成字节时,如果最后一个字节没有用满,就向上取整:

ceil(M × nbits / 8)

进入 IVF 倒排结构后,还要附带前述向量 ID,所以完整的每条公式是:

ceil(M × nbits / 8) + 8

这里不能遗漏 ceil,因为编码是按位组织,而存储单元按字节计。也不能在同一公式外再追加 4 × d:官方 IndexIVFPQ 行给出的已经是压缩编码加向量 ID,不是“完整向量加 PQ 编码”。

作为对照,不使用倒排表的 IndexPQ 每条字节数是:

ceil(M × nbits / 8)

它没有 IVF 行中的 +8。PQ 码本本身也不包含在这个每条向量公式里,不能把码本、训练过程或实现附加项折算成未经给出的固定字节数。

IVF 标量量化:d/2 + 8、d + 8、2 × d + 8

IndexIVFScalarQuantizer 同样把量化表示放入倒排表,因此每条向量还要附带 8 字节向量 ID。

Faiss 官方 wiki 列出的公式分别是:

  • SQ4:d/2 + 8
  • SQ8:d + 8
  • SQfp16:2 × d + 8

这些名称对应的是 IVF 标量量化行,不能把 IVF 的 +8 自动带到 flat 标量量化中。官方 IndexScalarQuantizer 的 flat 模式另有自己的 Bytes/vector 公式。

标量量化压缩的是倒排项中的向量表示。SQ4、SQ8 与 SQfp16 的每条字节数可以直接计算,额外的 8 则单独记在向量 ID 上。码本及其他未由该公式展开的部分仍属于下限估算之外。

HNSW:4 × d + x × M × 2 × 4

IndexHNSWFlat 仍以 flat 存储作为底层向量表示,所以向量本体是:

4 × d

HNSW 还要保存图中的邻居连接。官方给出的每条字节公式为:

4 × d + x × M × 2 × 4

其中:

  • M 是图中使用的邻居数。
  • x 是图内层数。
  • x × M × 2 × 4 是官方公式中的邻居表开销。

M 增大时,这部分会增大。Faiss 官方 wiki 也明确指出,更大的 M 会使用更多内存。

但不能把公式末项的 4 脱离来源,宣布它是所有 HNSW 实现的通用边字节数。这里能采用的是 Faiss 官方给出的完整公式 x × M × 2 × 4,不能把其中的因子移植成跨实现的行业常数。

官方只给公式而不给统一倍数,原因也很直接:M 是具体参数,x 取决于实际建出的层数。公式说明了额外内存怎样随参数变化,却没有给出“某个参数变化后,整库内存变成多少倍”的通用结论。

只有在 d、M 和 x 都已知时,这段公式才能作为纯算术精确算出。缺少层数信息时,只能保留公式,不能补一个看似精确的系数。

HNSW 为什么要分层

HNSW 原论文描述的是多层、近似图结构:每层都是一组邻近图,各层对应存储元素的不同嵌套子集。元素所在的最高层按指数衰减的概率分布随机选择。

搜索可以从上层开始,再进入更低层细化。上层承担较粗的导航,下层保存更细的邻接关系。HNSW 是纯图结构,不依赖近似图方法通常另外设置的粗搜结构。

落到内存账上,除了向量本体,还要保存各层图中的连接。层是嵌套子集,并不意味着每个向量都在每一层出现;向量涉及哪些层、图最终形成多少连接,都与实际建图过程有关。

这正是 x 不能被替换成固定层数的原因。HNSW 没有额外的独立粗搜结构,也不代表没有额外存储:图本身就是裸向量之外的开销,连接边则随 M 和层数变化。

把每条公式换成整批容量

设条数为 N,按 Faiss 官方 Bytes/vector 模型乘开后,向量载荷下限分别为:

  • flat:N × 4 × d
  • IVF Flat:N × (4 × d + 8)
  • HNSW Flat:N × (4 × d + x × M × 2 × 4)
  • flat PQ:N × ceil(M × nbits / 8)
  • IVF-PQ:N × (ceil(M × nbits / 8) + 8)
  • IVF-SQ4:N × (d/2 + 8)
  • IVF-SQ8:N × (d + 8)
  • IVF-SQfp16:N × (2 × d + 8)

flat、IVF Flat、PQ 和 IVF 标量量化都可以在参数齐全时直接计算。HNSW Flat 的整批公式只有在 M 与 x 明确时才能闭合;否则应保留未计算状态,而不是猜测层数。

这些整批结果仍是公式覆盖部分的载荷,不是数据库进程的完整内存读数。

确定性分级:把每笔字节放回对应账本

公式算得准的部分

给定公式所需参数后,下列项目属于纯算术,可以精确到字节:

  • flat 的 4 × d
  • IVF Flat 的 +8 向量 ID
  • flat PQ 的 ceil(M × nbits / 8)
  • IVF-PQ 的 ceil(M × nbits / 8) + 8
  • IVF-SQ4 的 d/2 + 8
  • IVF-SQ8 的 d + 8
  • IVF-SQfp16 的 2 × d + 8
  • HNSW 已知 M 与 x 时的 x × M × 2 × 4

“公式算得准”指的是公式项可以准确计算,不代表完整系统占用已经全部算完。

按公开算法结构可以推出的部分

公开结构能够确认开销来自哪里:

  • HNSW 是嵌套子集上的多层邻近图,除了向量还有连接边。
  • HNSW 的邻居连接随 M 和层数变化。
  • IVF 把向量放入倒排列表,并为倒排项保存向量 ID。
  • PQ 把向量拆成子向量,再按 nbits 形成压缩编码。

这些结论可以解释为什么要加账,但不能替代缺失的参数与实现数据。

本站算不准的部分

只看维度、条数、精度和索引名称,未必能得到 HNSW 的 M 与 x、PQ 的 M 与 nbits,也无法从公开公式推出所有实现附加项。

因此,本站不会继续给出:

  • HNSW 的通用内存放大倍数
  • 缺少 x 时猜出的层数
  • 由 nlists 推导的固定额外字节
  • PQ 码本或实现开销的统一系数
  • 公式未覆盖部分的固定占比

pgvector 官方仓库提供了一个真实数据库口径的旁证:它明确写明 HNSW 使用多层图,并且比 IVFFlat 使用更多内存。这个结论只能确认容量方向,不能证明 Faiss 公式在所有实现中等量成立,更不能补出 HNSW 的统一内存倍数。

容量账的结论

先摘出 4 × d 的完整向量载荷,再按索引结构单独计算:flat 不加图结构,IVF 加向量 ID,HNSW 加随 M 与层数变化的邻居连接,IVF-PQ 和 IVF-SQ 则改用量化编码公式。

公式能算准的部分精确到字节;公开结构能解释开销来自哪里;随参数和实现变化的部分只保留公式,不给倍数。所有公式结果都是下限估算,真实占用只会更高。

计算字段与取整口径见 /rules/,工具能够接收哪些参数见 /articles/ 的使用边界,站点为何坚持下限估算见 /about/。

本文为容量测算方法说明,不构成选型建议。