• 文章
  • 向量搜索资源优化指南
返回向量搜索手册

向量搜索资源优化指南

David Myriel

·

February 09, 2025

Vector Search Resource Optimization Guide

本指南内容提要

资源管理策略: 如果您试图在预算有限的情况下扩展您的应用,本指南将为您提供帮助。我们将向您展示如何避免浪费计算资源,并获得投资的最大回报。

性能改进技巧: 我们将深入探讨索引、压缩和分区等高级技术。我们的技巧将帮助您在大规模应用中获得更好的结果,同时降低总资源消耗。

查询优化方法: 优化您的向量数据库设置不仅是为了节省成本。我们将向您展示如何构建能够在保持灵活性的同时提供持续高精度的搜索系统。


记住:优化是一门平衡的艺术

在本指南中,我们将向您展示如何利用 Qdrant 的功能来满足您的性能需求。然而,资源优化需要权衡,您无法兼得所有优点。选择最适合您目标的优化策略取决于您。

optimization

让我们看看一些常见的优化目标和策略

阅读完本文后,请查阅我们文档中关于 Qdrant 的优化方法 的代码示例。


配置索引以加快搜索速度

indexing

向量索引是 Qdrant 计算向量相似度的核心位置。它是您的搜索过程的骨干,从海量数据中检索相关结果。

Qdrant 使用 HNSW(分层可导航小世界图)算法 作为其稠密向量索引,它既强大又可扩展。

图 2: 一个包含三层的 HNSW 向量索引示例。跟随顶层蓝色箭头,查看查询如何在数据库索引中遍历。最接近的结果位于底层,最靠近灰色查询点。

hnsw

向量索引优化参数

处理包含数十亿向量的海量数据集需要大量资源,这些资源价格不菲。虽然 Qdrant 提供了合理的默认设置,但根据您的特定用例进行调整可以释放最佳性能。以下是您需要了解的内容。

以下参数使您能够灵活地根据您的特定工作负载微调 Qdrant 的性能。您可以直接在 Qdrant 的 配置 文件或在集合和命名向量级别修改它们,以实现更精细的控制。

图 3: 三个关键 HNSW 参数的描述。

hnsw-parameters

1. 参数 m 确定每个节点的边数

这控制着图中的边数。值越高,搜索精度越高,但需要更多内存和构建时间。请微调此参数以平衡内存使用和精度。

2. 参数 ef_construct 控制索引构建范围

此参数设置在索引构建期间考虑的邻居数量。值越大,索引的准确性越高,但会增加构建时间。使用此参数可自定义您的索引速度与质量的权衡。

您需要在创建集合时设置 mef_construct 参数

client.update_collection(
    collection_name="{collection_name}",
    vectors_config={
        "my_vector": models.VectorParamsDiff(
            hnsw_config=models.HnswConfigDiff(
                m=32,
                ef_construct=123,
            ),
        ),
    }
)

3. 参数 ef 更新向量搜索范围

这决定了在搜索查询期间评估的邻居数量。您可以调整此参数以平衡查询速度和准确性。

参数 ef 在搜索过程中配置

client.query_points(
   collection_name="{collection_name}",
   query=[...]
   search_params=models.SearchParams(hnsw_ef=128, exact=False),
)

这些只是 HNSW 的基础知识。了解更多关于 索引 的信息。


数据压缩技术

compression

高效数据压缩是向量数据库资源优化的基石。通过减少内存使用,您可以实现更快的查询性能,而不会牺牲太多准确性。

一种强大的技术是 量化,它将高维向量转换为紧凑表示,同时保留相对相似性。让我们探讨 Qdrant 中可用的量化选项。

标量量化

标量量化在压缩和性能之间实现了出色的平衡,使其成为大多数用例的首选。

此方法最小化用于表示每个向量分量的位数。例如,Qdrant 将 32 位浮点值 (float32) 压缩为 8 位无符号整数 (uint8),将内存使用量大幅减少 75%。

图 4: 上面的示例显示了一个大小为 40 字节的 float32 向量。将其转换为 int8 格式可将其大小减小四倍,同时保持向量之间的近似相似性关系。与原始表示相比,精度损失对于大多数实际应用通常可以忽略不计。

scalar-quantization

标量量化的优势

优势描述
内存使用量将下降压缩将内存使用量减少 4 倍。Qdrant 将 32 位浮点值 (float32) 压缩为 8 位无符号整数 (uint8)。
准确性损失很小从 float32 转换为 uint8 会引入少量精度损失。典型错误率保持在 1% 以下,这使得此方法非常高效。
最适合特定用例适用于对少量精度损失可接受的高维向量。

在创建集合时设置

client.create_collection(
   collection_name="{collection_name}",
   vectors_config=models.VectorParams(size=1536, distance=models.Distance.COSINE),
   quantization_config=models.ScalarQuantization(
       scalar=models.ScalarQuantizationConfig(
           type=models.ScalarType.INT8,
           quantile=0.99,
           always_ram=True,
       ),
   ),
)

使用 Qdrant 时,您可以微调量化配置以优化精度、内存使用和性能。以下是关键配置选项包括

配置选项描述
类型指定量化向量类型(当前仅支持 int8)。
分位数设置量化的边界,排除异常值。例如,0.99 排除最高 1% 的极端值,以保持更好的准确性。
始终驻留内存将量化向量保存在内存中以加快搜索速度。

调整这些设置以在您的特定工作负载下,在精度和效率之间取得适当的平衡。


了解更多关于 标量量化 的信息


二进制量化

二进制量化 将标量量化提升到一个新的水平,将每个向量分量压缩成仅 一位。这种方法实现了无与伦比的内存效率和查询速度,将内存使用量减少 32 倍,并使搜索速度提高多达 40 倍。

二进制量化的优势

二进制量化非常适合大规模数据集和兼容的嵌入模型,在这些场景下,压缩和速度至关重要。

图 5: 此方法实现了最大压缩。它将内存使用量减少 32 倍,并将搜索速度提高多达 40 倍。

binary-quantization
优势描述
高效的相似性计算通过点积比较模拟汉明距离,使其快速高效。
非常适合高维向量与 OpenAI 的 text-embedding-ada-002 或 Cohere 的 embed-english-v3.0 等嵌入模型配合良好。
精度管理考虑使用重新评分或过采样来弥补精度损失。

以下是您如何在 Qdrant 中启用二进制量化

client.create_collection(
   collection_name="{collection_name}",
   vectors_config=models.VectorParams(size=1536, distance=models.Distance.COSINE),
   quantization_config=models.BinaryQuantization(
       binary=models.BinaryQuantizationConfig(
           always_ram=True,
       ),
   ),
)

默认情况下,量化向量像原始向量一样加载,除非您将 always_ram 设置为 True 以实现即时访问和更快的查询。


了解更多关于 二进制量化 的信息


扩展数据库

sharding

在像 Qdrant 这样的分布式系统中高效管理大型数据集需要智能的数据隔离策略。多租户分片 是帮助您处理大量用户特定数据,同时保持性能和可伸缩性的重要工具。

多租户

多租户 是一种软件架构,其中多个独立用户(或租户)共享相同的资源或环境。在 Qdrant 中,使用逻辑分区的单个集合通常是多租户用例最有效的设置。

图 5: 每个单独的向量都分配了一个特定的负载(payload),表示它属于哪个租户。这就是大量不同租户可以共享一个 Qdrant 集合的方式。

multitenancy

为什么选择多租户?

  • 逻辑隔离:确保每个租户的数据在同一集合中保持独立。
  • 最小化开销:与为每个用户维护单独的集合相比,减少了资源消耗。
  • 可伸缩性:处理高用户量而不会影响性能。

以下是您如何在 Qdrant 中高效实现多租户

client.create_payload_index(
    collection_name="{collection_name}",
    field_name="group_id",
    field_schema=models.KeywordIndexParams(
        type="keyword",
        is_tenant=True,
    ),
)

创建关键字负载索引,并将 is_tenant 参数设置为 True,可以修改向量的逻辑存储方式。存储结构将被组织起来,以便将同一租户的向量放在一起。

现在,存储在 Qdrant 中的每个点都应设置 group_id 负载属性

client.upsert(
   collection_name="{collection_name}",
   points=[
       models.PointStruct(
           id=1,
           payload={"group_id": "user_1"},
           vector=[0.9, 0.1, 0.1],
       ),

       models.PointStruct(
           id=2,
           payload={"group_id": "user_2"},
           vector=[0.5, 0.9, 0.4],
       )
   ]
)

为了确保多租户环境中的数据适当隔离,您可以为每个向量分配一个唯一标识符,例如 group_id。这种方法确保每个用户的数据保持隔离,允许用户只访问自己的数据。您还可以通过在查询期间应用过滤器来进一步增强此设置,以限制对相关数据的访问。


了解更多关于 多租户 的信息


分片

分片是 Qdrant 中的一项关键策略,用于将集合分割成更小的单元,称为 分片,以便高效地将数据分布到多个节点上。它是提高大型系统可伸缩性和保持性能的强大工具。

用户定义的分片

用户定义的分片 允许您通过指定分片键来控制数据放置。此功能在多租户设置中特别有用,因为它可以将每个租户的数据隔离在单独的分片中,从而确保更好的组织和增强的数据安全性。

图 6: 用户可以在同一集合内对与其相关的分片进行 upsert 和查询。区域分片有助于避免跨洲际流量。

user-defined-sharding

示例

client.create_collection(
    collection_name="my_custom_sharded_collection",
    shard_number=1,
    sharding_method=models.ShardingMethod.CUSTOM
)
client.create_shard_key("my_custom_sharded_collection", "tenant_id")

在 Qdrant 中实现用户定义的分片时,有两个关键参数对于实现高效的数据分布至关重要

  1. 分片键:

    分片键决定了数据点如何在分片间分布。例如,使用 tenant_id 这样的键可以控制 Qdrant 如何对数据进行分区。添加到集合中的每个数据点将根据此键的值分配到一个分片,从而确保数据的逻辑隔离。

  2. 分片数量:

    这定义了每个分片键的物理分片总数,影响资源分配和查询性能。

以下是如何向使用用户定义分片的集合添加数据点

client.upsert(
    collection_name="my_custom_sharded_collection", 
    points=[
        models.PointStruct(
            id=1111, 
            vector=[0.1, 0.2, 0.3]
        )
    ], 
    shard_key_selector="tenant_1"
)

这段代码根据 tenant_1 分片键将点分配到特定分片,确保数据正确放置。

以下是如何选择分片数量

建议描述
将分片数量与节点数量匹配分片数量应与集群中的节点数量保持一致,以平衡资源利用和查询性能。
规划可伸缩性至少从 每个节点 2 个分片 开始,为未来增长留出空间。
面向未来从大约 12 个分片 开始是一个经验法则。这种设置允许您的系统从 1 个节点无缝扩展到 12 个节点,而无需重新分片。

了解更多关于 分布式部署中的分片 的信息


查询优化

qdrant 处理大型数据集和复杂查询时,提高向量数据库性能至关重要。通过利用诸如 过滤批量处理重排序重新评分过采样 等技术,您可以确保快速响应时间,并在大规模应用中保持效率。

过滤

过滤允许您在查询结果中仅选择所需字段。通过限制输出大小,您可以显着减少响应时间并提高性能。

可过滤向量索引是 Qdrant 解决预过滤和后过滤问题的方法,它通过向搜索图添加特殊链接来实现。它旨在在允许精确过滤的同时保持向量搜索的速度优势,解决在向量搜索后应用过滤器可能出现的效率低下问题。

示例

results = client.search(
    collection_name="my_collection",
    query_vector=[0.1, 0.2, 0.3],
    query_filter=models.Filter(must=[
        models.FieldCondition(
            key="category",
            match=models.MatchValue(value="my-category-name"),
        )
    ]),
    limit=10, 
)

图 7: 可过滤向量索引向搜索图添加特殊链接以加快遍历速度。

filterable-vector-index

可过滤向量索引:这项技术在剩余数据点之间构建额外的链接 (橙色)。保留下来的过滤点现在可以再次遍历。Qdrant 使用特殊的基于类别的方法连接这些数据点。


阅读更多关于 过滤文档 的信息,并查看 完整的过滤指南


批量处理

批量处理将多个操作合并到一个执行周期中,减少了请求开销并提高了吞吐量。这对于数据插入和查询执行都是一种有效的策略。

batch-processing

批量插入:与其单独插入向量,不如将它们分组为中等大小的批次,以最小化数据库请求次数和频繁写入的开销。

示例

vectors = [
   [.1, .0, .0, .0],
   [.0, .1, .0, .0],
   [.0, .0, .1, .0],
   [.0, .0, .0, .1],
   
]
client.upload_collection(
   collection_name="test_collection",
   vectors=vectors,
)

这减少了写入操作并确保更快的数据摄入。

批量查询:类似地,您可以将多个查询批量处理在一起,而不是一个一个地执行。这减少了到数据库的往返次数,优化了性能并降低了延迟。

示例

results = client.search_batch(
   collection_name="test_collection",
   requests=[
       SearchRequest(
           vector=[0., 0., 2., 0.],
           limit=1,
       ),
       SearchRequest(
           vector=[0., 0., 0., 0.01],
           with_vector=True,
           limit=2,
       )
   ]
)

批量查询在处理大量相似查询或同时处理多个用户请求时特别有用。


混合搜索结合了 关键字过滤向量相似性搜索,从而实现更快、更精确的结果。关键字有助于快速缩小数据集范围,而向量相似性确保了语义准确性。这种搜索方法结合了 稠密向量和稀疏向量

Qdrant 中的混合搜索同时使用融合(fusion)和重排序(reranking)。前者是关于结合来自不同搜索方法的结果,仅基于每种方法返回的分数。这通常涉及一些归一化,因为不同方法返回的分数可能在不同范围内。

图 8:混合搜索架构

hybrid-search

之后,有一个公式可以获取相关性度量并计算最终分数,我们稍后用它来对文档进行重排序。Qdrant 内置支持 Reciprocal Rank Fusion 方法,这是该领域的实际标准。


了解更多关于 混合搜索 的信息,并阅读 混合查询文档


过采样

过采样是一种有助于弥补因量化而损失的任何精度的方法。由于量化简化了向量,在初始搜索中可能会遗漏一些相关的匹配项。为避免这种情况,您可以 检索更多候选结果,从而增加最相关的向量进入最终结果的机会。

您可以通过设置 oversampling 参数来控制额外的候选结果数量。例如,如果您期望的结果数量(limit)为 4,并且设置 oversampling 因子为 2,Qdrant 将检索 8 个候选结果(4 × 2)。

您可以调整过采样因子来控制 Qdrant 在初始池中包含多少额外向量。更多候选结果意味着更有可能获得高质量的 top-K 结果,尤其是在使用原始向量进行重新评分之后。


了解更多关于 过采样 的信息。


重新评分

在通过过采样收集更多潜在匹配项后,会根据附加标准重新评估每个候选结果,以确保更高的准确性和与查询的相关性。

重新评分过程将量化向量映射回其对应的原始向量,使您能够考虑诸如上下文、元数据或未包含在初始搜索中的附加相关性等因素,从而获得更准确的结果。

重新评分和过采样的示例:

client.query_points(
    collection_name="my_collection",
    query_vector=[0.22, -0.01, -0.98, 0.37],
    search_params=models.SearchParams(
        quantization=models.QuantizationSearchParams(
            rescore=True,   # Enables rescoring with original vectors
            oversampling=2  # Retrieves extra candidates for rescoring
        )
    ),
    limit=4  # Desired number of final results
)

了解更多关于 重新评分 的信息。


重排序

重排序根据附加标准调整搜索结果的顺序,确保最相关的结果优先显示。

这种方法是将来自不同搜索方法的结果,并根据使用文档内容而非仅分数的额外处理进行重新排序。这种处理可能依赖于额外的神经模型,例如交叉编码器,该模型在整个数据集上使用会效率低下。

reranking

这些方法仅在用于由更快搜索方法返回的较小候选子集时才实际适用。在这种情况下,后期交互模型(例如 ColBERT)效率要高得多,因为它们可以用于对候选结果进行重排序,而无需访问集合中的所有文档。

示例

client.query_points(
       "collection-name",
       prefetch=prefetch, # Previous results
       query=late_vectors, # Colbert converted query
       using="colbertv2.0",
       with_payload=True,
       limit=10,
)

了解更多关于 重排序 的信息。


存储:磁盘对比内存

disk

存储描述
内存对于快速访问常用数据(如索引向量)至关重要。所需内存量可以根据您的数据集大小和维度进行估算。例如,存储 100 万个维度为 1024 的向量 大约需要 5.72 GB 内存
磁盘适用于不经常访问的数据,如负载(payload)和非关键信息。基于磁盘的存储降低了内存需求,但可能会引入轻微延迟。

哪种磁盘类型?

推荐使用 本地 SSD 来获得最佳性能,因为它们提供最快的查询响应时间且延迟最小。虽然网络附加存储也可行,但通常会引入可能影响性能的额外延迟,因此在可能的情况下优先选择本地 SSD,特别是对于需要高速随机访问的工作负载。

向量和负载(Payload)的内存管理

随着数据规模的扩大,有效的资源管理对于在保持低成本的同时确保您的应用保持可靠和高性能至关重要。需要关注的关键领域之一是 内存管理

了解 Qdrant 如何处理内存可以帮助您就如何扩展向量数据库做出明智的决定。Qdrant 支持两种主要的向量存储方法

1. 内存存储

  • 工作原理:所有数据存储在内存中,为查询和操作提供最快的访问时间。
  • 何时使用:此设置非常适合对性能要求极高的应用,并且您的内存容量可以容纳所有数据。
  • 优点:查询和更新速度最快。
  • 局限性:随着数据集的增长,内存使用量可能成为瓶颈。

2. 内存映射存储

  • 工作原理:内存映射存储不是将所有数据加载到内存中,而是将数据文件直接映射到磁盘上的虚拟地址空间。系统页面缓存处理数据访问,使其效率很高。
  • 何时使用:非常适合存储超出可用内存的大型集合,同时在有足够内存时仍能保持接近内存的性能。
  • 优点:平衡了性能和内存使用,使您能够处理大于物理内存的数据集。
  • 局限性:比纯内存存储稍慢,但可伸缩性显著提高。

要在 Qdrant 中启用内存映射向量存储,您可以在创建或更新集合时将 on_disk 参数设置为 true

client.create_collection(
   collection_name="{collection_name}",
   vectors_config=models.VectorParams(
      
      on_disk=True
   )
)

对负载(Payload)执行相同操作

client.create_collection(
    collection_name="{collection_name}",
    on_disk_payload= True
)

在 Qdrant 中选择存储方法的通用指南是,当高性能是首要任务且有足够的内存容纳数据集时,使用 内存存储。这种方法通过将数据保存在内存中以便随时访问,确保了最快的访问速度。

然而,对于大型数据集或内存受限的场景,内存映射磁盘存储 更适合。这些方法通过将数据存储在磁盘上,并利用页面缓存和索引等高级技术来保持高效且相对快速的数据访问,从而显著减少内存使用量。

监控数据库

monitoring

持续监控对于维护系统健康并在潜在问题升级之前识别它们至关重要。诸如 PrometheusGrafana 之类的工具被广泛用于实现此目的。

  • Prometheus:一个开源的监控和警报工具包,Prometheus 在时间序列数据库中收集和存储指标。它从预定义的端点抓取指标,并支持强大的查询和可视化功能。
  • Grafana:Grafana 通常与 Prometheus 搭配使用,提供了一个直观的界面,用于可视化指标和创建交互式仪表板。

Qdrant 通过 /metrics 端点以 Prometheus/OpenMetrics 格式暴露指标。Prometheus 可以抓取此端点以监控 Qdrant 系统的各个方面。

对于本地 Qdrant 实例,指标端点通常可在以下地址获取

http://localhost:6333/metrics

以下是一些需要监控的重要指标

指标名称含义
集合总数所有集合中的向量总数
REST API 的平均响应时长(秒)gRPC API 的平均响应时长(秒)
rest_responses_avg_duration_secondsREST API 的平均响应时长
grpc_responses_avg_duration_secondsgRPC API 的平均响应时长
失败的响应总数(REST)REST 失败响应总数

阅读更多关于 Qdrant 开源监控Qdrant 云监控 的信息。


回顾:何时应进行优化?

solutions

场景描述
扩大规模时随着数据增长和请求激增,优化资源使用可以确保您的系统即使在高负载下也能保持响应性和成本效益。
面临预算限制时在性能和成本之间取得完美平衡,削减不必要的开支,同时保持基本功能。
需要更好的性能时如果您注意到查询速度慢、延迟问题或频繁超时,是时候微调您的资源分配了。
系统稳定性至关重要时为了管理高流量环境,您需要防止因资源耗尽导致的崩溃或故障。

获取速查表

想下载本指南的打印友好版本吗?立即下载。

downloadable vector search resource optimization guide

此页面有用吗?

感谢您的反馈!🙏

很抱歉让您失望了。😔 您可以在 GitHub 上 编辑 此页面,或者 创建 一个 GitHub issue。