没必要为向量数据库支付高昂的费用

geekbing2025-09-02AI向量数据库

几个月前因为错误的选择了嵌入模型的维度,导致多花了不少冤枉钱。最近也有朋友问我怎么选择嵌入模型的维度,看到网上有人甚至为一个大概只有 100 万向量的无服务器向量数据库每月支付超过 70 美元。随着数据的增长,在达到千万、上亿向量的时候,这个成本不敢想象。

向量数据库在推荐系统、语义搜索和问答系统中表现出色。但随着数据量不断增长,成本也会跟着水涨船高。如果我告诉你,对于向量数量只有几千万甚至上亿的数据集,你其实完全没必要为高级向量数据库花这个钱。

下面简单介绍几个概念:

  1. Matryoshka 俄罗斯套娃(MRL):一种模型训练技术,让模型能够把高维向量嵌套在低维向量里面,这样你就可以把大的向量裁剪到 64 维甚至 32 维,而且性能损失不会太大。
  2. 在 Qdrant 创建向量集合时,只需要简单设置open in new window,就可以轻松处理数百万级别的向量,同时不会让内存占用急剧膨胀。
  3. 实际观测结果:500 万条 64 维向量只需约 0.5-1 GB 内存,可在月租 4 欧元的 VPS 上运行,而传统方案需要数十倍的资源和成本。
  4. 重排序与混合搜索:如何通过交叉编码器或基于关键词的兜底方案,来弥补量化带来的精度损失。

俄罗斯套娃表示学习(MRL)

在聊向量数据库或索引之前,我们先来搞清楚一个问题,为什么把向量压缩到 64 维后,检索性能还能保持得这么好?这背后的秘诀就是俄罗斯套娃表示学习(MRL)。

什么是 MRL?

MRL 的训练方式让嵌入模型能够把高维向量整齐地嵌套在低维子集里面,就像俄罗斯套娃一样。如果模型输出的是 1024 维向量,你可以直接截断成 768 维、256 维或者 64 维,而且每个子集依然是语义上有意义的表示。

这种方法已经被用在了一些顶尖的多语言嵌入模型上,比如 Jina Embeddings v3open in new window,以及 OpenAI 的部分最新嵌入模型。这类模型默认可能会输出 1024 维甚至更高维度的向量,但你完全可以根据模型的训练方式,只存储前 64 维、128 维或 256 维就够了。不过需要注意的是,不是所有嵌入模型都支持 MRL。

MRL 的优势

  1. 灵活性:不需要为不同的向量维度单独训练多个模型,一个模型就能输出多种维度级别的向量。
  2. 成本与速度:向量越小,检索时所需的存储空间和 CPU/GPU 开销就越少。如果你有数百万条向量,把维度从 1024 维压缩到 64 维,对云服务账单和查询延迟的影响可以说是颠覆性的。
  3. 平滑降级:得益于 MRL 的训练方式,性能下降的过程通常比暴力截断维度或随机投影要平缓得多。即使压缩到 64 维,语义信息的保留程度也会出乎意料地好。

jina-embeddings-v3.png

上图展示了 jina-embeddings-v3 在不同向量维度下的表现。随着维度增加,语义相似度和检索效果都会有所提升,但超过 256 维后提升已经不明显了。即使只用 64 维,性能也没有掉太多,检索效果只比最高维度低了大约 4~5 个点。

为什么需要重排序或混合检索

包括基于 MRL 的方法在内,各种压缩技术都可能略微降低召回率和精确率。不过在很多实际应用场景中,尤其是 RAG 工作流里,通常会采用两阶段流水线来解决这个问题:

  1. 检索阶段:用压缩或量化后的索引快速捞出top-k候选结果。这个阶段的目标是快和省钱,即使牺牲一点精度也没关系。
  2. 重排序阶段:对top-k结果做进一步的相关性精排,可以使用交叉编码器重排序模型(如 Cohere、Jina 或 OpenAI 的 Reranker)。这些模型计算开销更大,但精度更高,通过对候选结果和查询之间的关系进行深度分析来打分排序。

这套流水线可以有效弥补激进降维带来的召回损失。高效检索加上进阶重排序的组合,即使在复杂场景下也能保证最终输出的相关性。

尤其是结合 BM25(关键词)检索(也叫稀疏检索)和重排序一起用,即使对向量做了量化压缩,整体检索效果依然非常强大。

standard_retrieval.png

上图对比了不同检索策略的效果。只使用 Embedding 检索时,平均失败率约为 5.7%;在此基础上加入 BM25 关键词检索后,失败率下降到 5.0%。如果再加入 Reranker 进行重排序,失败率可以进一步降到 3.5%。整体来看,通过 Hybrid Search(Embedding + BM25)并结合 Rerank,可以明显提高检索的准确率。

使用 Qdrant 创建集合

既然你已经了解了如何通过 MRL 获得64维(甚至更低维度)的向量,接下来我们把它们存进一个低成本的向量集合里。创建集合时的设置如下:

  • 完全禁用索引,延迟 HNSW 图构建,启用 int8 量化,向量数据和 HNSW 索引都启用磁盘存储,支持混合检索(密集+稀疏向量)。

注意:我们假设已经用一个支持 MRL 的模型生成了 500 万条向量,并将其压缩到了64维(当然也可以用其他 MRL 维度,比如 128 维或 256 维)。

安装依赖

pip install \
  llama-index-core \
  llama-index-embeddings-jinaai \
  llama-index-postprocessor-jinaai-rerank \
  llama-index-vector-stores-qdrant \
  qdrant-client \
  python-dotenv \
  tiktoken

启动 Qdrant 服务实例

# 启动 Qdrant 服务
docker run -p 6333:6333 qdrant/qdrant
# 连接 Qdrant
client = QdrantClient(host="localhost", port=6333)

创建集合

client.create_collection(
        collection_name=data.name,
        # 混合检索配合 BM25 使用才开启
        vectors_config={
            "text-dense": models.VectorParams(
                size=64, # 向量维度
                distance=models.Distance.COSINE, # 距离度量方式
                on_disk=True, # 是否将向量存储在磁盘上
            )
        },
        sparse_vectors_config={
            "text-sparse": models.SparseVectorParams(
                index=models.SparseIndexParams(on_disk=True) # 是否将稀疏索引存储在磁盘上
            )
        },
        quantization_config=models.ScalarQuantization(
            scalar=models.ScalarQuantizationConfig(
                type=models.ScalarType.INT8,  # 量化类型
                quantile=0.99,  # 量化分位数,排除 1% 极端异常值,保持量化精度
                always_ram=True,  # 是否始终将量化后的向量存储在内存中
            ),
        ),
        hnsw_config=models.HnswConfigDiff(
            m=0,
            # 数据嵌入完成后,将 hnsw_config: { "m": 0 } 还原为 16, on_disk: False
            on_disk=True, # 是否将 HNSW 图数据存储在磁盘上,批量嵌入时开启,嵌入完成后关闭
        ),
        # 创建集合时的优化器配置
        optimizers_config=models.OptimizersConfigDiff(
            indexing_threshold=0,  # 批量嵌入过程中禁用索引更新, 上传完成之后还原成 20000
        ),
    )

配置参数详解

上面的代码采用了两阶段配置策略,在批量导入和正常使用时使用不同的参数:

批量导入阶段(当前配置)

  • m=0: 完全禁用 HNSW 图构建,避免每次插入都更新索引,大幅提升导入速度
  • indexing_threshold=0: 禁用自动索引触发,防止导入过程中的性能抖动
  • on_disk=True: 将原始向量和 HNSW 图存储在磁盘,仅保留量化向量在内存(节省 75% 内存)
  • always_ram=True: 量化向量始终保留在内存中,保证查询性能

导入完成后的配置恢复

数据导入完成后,执行以下操作恢复索引性能,否则查询延迟会非常高。

from qdrant_client import models
# 恢复 HNSW 索引配置
client.update_collection(
    collection_name="your_collection",
    hnsw_config=models.HnswConfigDiff(
        m=16,  # 恢复 HNSW 图构建,16 是推荐值(平衡精度和速度)
        on_disk=False  # 将 HNSW 图移到内存,加快查询速度
    ),
    optimizers_config=models.OptimizersConfigDiff(
        indexing_threshold=20000  # 恢复自动索引,每 2 万条向量触发一次优化
    )
)

量化(Quantization)

量化是 Qdrant 的一个可选功能,可以让高维向量的存储和检索更加高效。通过把原始向量转换成新的表示形式,量化在压缩数据的同时,还能尽量保留向量之间原有的相对距离。

量化本身存在一定的取舍。一方面,量化可以大幅降低存储需求、加快检索速度,在那些对资源消耗非常敏感的大规模应用场景里,这个优势尤为突出。另一方面,量化会引入近似误差,可能导致检索质量略有下降。这种取舍的程度取决于量化方法及其参数,以及数据本身的特性。

下面简单介绍一下不同量化方式的差异,Qdrant 官方文档open in new window有更详细的说明。

标量量化(Scalar Quantization)

Qdrant 默认用32位浮点数来表示原始向量的每个分量。标量量化可以把位数压缩到8位,也就是说,Qdrant 会对每个向量分量做float32 -> uint8的转换。这样一来,存储一条向量所需的内存直接缩小到原来的四分之一。除了减少内存占用,标量量化还能加快检索速度。

标量量化的主要缺点是精度损失。float32 -> uint8的转换会引入误差,可能导致检索质量略有下降。不过这个误差通常可以忽略不计,而且对于高维向量来说影响往往更小。据 Qdrant 官方说,标量量化引入的误差通常不超过 1%。

二值量化(Binary Quantization)

二值量化是标量量化的一种极端形式,它把每个向量分量压缩成一个比特位,内存占用直接缩小到原来的三十二分之一。

这也是速度最快的量化方法,因为只需要几条 CPU 指令就能完成向量比较。

与原始向量相比,二值量化最高可以实现 40 倍的速度提升。

不过,二值量化只在高维向量上效果好,而且要求向量分量的分布接近中心对称。

维度较低或向量分量分布不符合要求的模型,可能需要额外做实验来找到最优的量化参数。

我们建议在开启重打分(rescoring)的情况下使用二值量化,这样只需付出很小的性能代价,就能显著提升检索质量。此外,还可以通过调整过采样倍数,在检索速度和检索质量之间灵活取舍。

乘积量化(Product Quantization)

乘积量化是一种通过把向量拆分成若干小块,再对每个块单独量化来压缩向量,从而减少内存占用的方法。

乘积量化的压缩比比标量量化更高,但也有一些取舍。乘积量化的距离计算对 SIMD 不友好,所以速度比标量量化慢。另外,乘积量化也存在精度损失,因此建议只在高维向量上使用。

如何选择合适的量化方法

下面是各种量化方法优缺点的简要对比表:

Quantization methodAccuracySpeedCompression
Scalar0.99up to x24
Product0.70.5up to 64
Binary (1 bit)0.95*up to x4032
Binary (1.5 bit)0.95**up to x3024
Binary (2 bit)0.95***up to x2016
  • *适用于支持高维向量的模型(约 1536 维以上)
  • **适用于支持中维向量的模型(约 1024 到 1536 维)
  • ***适用于支持低维向量的模型(约 768 到 1024 维)

二值量化是速度最快、内存效率最高的方法,但要求向量分量的分布接近中心对称,建议只在经过验证的嵌入模型上使用。

如果打算在低维或中维向量(约 512 到 1024 维)上使用二值量化,建议同时启用 1.5 位或 2 位量化以及非对称量化功能。

标量量化是最通用的方法,在精度、速度和压缩比之间取得了不错的平衡,如果二值量化不适用,推荐作为默认的量化方案。

乘积量化可以提供更高的压缩比,但精度损失较大,速度也比标量量化慢。如果内存占用是首要考量、检索速度不是关键因素,可以考虑使用。

量化技巧

  • 全部存在内存:默认原始向量和量化后的向量全部加载并保存在内存中,速度最快,但内存占有率很高。
  • 原始向量存磁盘,量化向量存内存:这是一种混合模式,在速度和内存占用之间取得较好的平衡。如果你希望在保持检索速度的同时减少内存占用,推荐使用这种方案。上面的代码正是采用的这种优化方案。
  • 全部存在磁盘:原始向量和量化后的向量全部存储在磁盘上,内存占用最小,但检索速度会相应下降。如果你的数据量很大,并且有较快的存储设备(比如 SSD 或 NVMe),推荐使用这种模式。

插入 500 万条 64 维向量

这一步就不做具体的展示了,下面有一段伪代码。

from llama_index.core import Document, VectorStoreIndex, StorageContext
from llama_index.vector_stores.qdrant import QdrantVectorStore
from llama_index.embeddings.jinaai import JinaEmbedding
import os

# LlamaIndex 包装 Qdrant(启用混合检索)
vector_store = QdrantVectorStore(
    client=client, 
    collection_name="your_collection",
    enable_hybrid=True,  # 启用混合检索(密集向量 + 稀疏向量)
    fastembed_sparse_model="Qdrant/bm25",  # 使用 BM25 稀疏向量模型
    batch_size=64,  # 批量大小
)
storage_context = StorageContext.from_defaults(vector_store=vector_store)

# 配置 Jina 嵌入模型 (64 维 MRL)
embed_model = JinaEmbedding(
    api_key=os.getenv("JINA_API_KEY"),
    model="jina-embeddings-v3",
    embed_batch_size=512,
    dimensions=64,
    task="retrieval.passage",
)

# 准备文档(实际应用中从数据库/文件分批加载,例如 5_000_000 条文档)
documents = load_documents_from_source()  # 你的数据来源

# LlamaIndex 自动处理嵌入和批量插入(同时生成密集向量和稀疏向量)
index = VectorStoreIndex.from_documents(
    documents,
    storage_context=storage_context,
    embed_model=embed_model,
    show_progress=True,  # 显示进度
)

实际效果

得益于 MRL,以及上面提到的各种优化措施,我们可以通过 Qdrant 的官方文章《Minimal RAM you need to serve a million vectors》open in new window来预估,存储和检索 500 万条 64 维向量只需要约 0.5-1 GB 的 RAM。这意味着它甚至可以轻松运行在一台月租 4 欧元、4GB RAM 的 netcup VPS 上,资源完全绰绰有余。

netcup_vps.png

重排序与混合检索

即使用了 MRL、降维处理以及量化,你可能还是会担心召回率有所损失。但请记住:

  • 在典型的 RAG 流水线里,你会先从向量索引里捞出 top-k 候选结果,再用交叉编码器(比如 Cohere、Jina 或 OpenAI 的模型)对结果做重排序。
  • 你也可以把基于关键词或符号的检索和压缩后的向量结合起来用,也就是混合检索,用来覆盖一些边缘情况,这也是上面引入混合检索的原因。

由于最终结果很大程度上取决于重排序器或混合检索逻辑,只要你的嵌入模型支持 MRL,使用 64 维向量带来的那点召回率损失基本可以忽略不计。这样你就能两全其美,既有低成本的检索,又有高精度的最终答案。

最后

对于向量数量只有 500 万甚至几千万的场景,根本不需要为高级向量数据库套餐付费。借助俄罗斯套娃表示学习(MRL)把向量压缩到 64 维甚至更低,再配合向量数据库各种优化设置,你就可以把内存占用控制在合理范围内,同时保持不错的查询速度。

当然,更精细的方案(比如在内存索引里使用 1024 维向量)精度可能更高,但成本也会高得多,速度也更慢。在实际应用中,如果你在做 RAG 这类高级任务,都会用交叉编码器对 top-k 结果做重排序,这本身就能弥补量化带来的精度损失。这意味着你完全可以在不花大钱堆基础设施的情况下,依然提供高质量的语义搜索、推荐系统或问答服务。

别再为专用向量数据库多花冤枉钱了。把基于 MRL 的向量、量化、磁盘索引结合起来,你可以在一台低成本 VPS 上轻松扩展到数千万级向量,延迟依然可控,加上交叉编码器重排序或混合检索的加持,检索质量也丝毫不打折。

扩展阅读

Bulk Upload Vectors to a Qdrant Collectionopen in new window

Optimizing Memory for Bulk Uploadsopen in new window

Minimal RAM you need to serve a million vectorsopen in new window

Optimizing Qdrant Performance: Three Scenariosopen in new window

Quantizationopen in new window

Best Practices in RAG Evaluation: A Comprehensive Guideopen in new window

Introducing Contextual Retrievalopen in new window

上次更新 3/10/2026, 10:22:51 AM
What do you think?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
Comments
  • Latest
  • Oldest
  • Hottest
Powered by Waline v2.15.8