本指南包含哪些内容?
资源管理策略: 如果您正在预算有限的情况下扩展您的应用程序——本指南正适合您。我们将向您展示如何避免浪费计算资源并最大限度地提高您的投资回报。
性能提升技巧: 我们将深入探讨索引、压缩和分区等高级技术。我们的技巧将帮助您在大规模应用中获得更好的结果,同时减少总资源开销。
查询优化方法: 改进您的向量数据库设置不仅仅是为了节省成本。我们将向您展示如何构建始终提供高精度且适应性强的搜索系统。
请记住:优化是一种权衡
在本指南中,我们将向您展示如何使用 Qdrant 的功能来满足您的性能需求。然而,资源存在权衡,您无法兼得。选择最适合您目标的优化策略取决于您。

让我们来看看一些常见的目标和优化策略
阅读完本文后,请查阅我们关于 Qdrant 优化方法文档中的代码示例。
配置索引以实现更快的搜索

向量索引是 Qdrant 计算向量相似度的中心位置。它是您搜索过程的骨干,从大量数据中检索相关结果。
Qdrant 使用 HNSW(Hierarchical Navigable Small World Graph)算法 作为其稠密向量索引,该算法既强大又可扩展。
图 2: 一个包含三层的 HNSW 向量索引示例。沿着顶层的蓝色箭头,可以看到查询如何在数据库索引中遍历。最接近的结果位于底层,靠近灰色查询点。

向量索引优化参数
处理包含数十亿向量的庞大数据集需要大量资源,而这些资源是有代价的。虽然 Qdrant 提供了合理的默认值,但根据您的特定用例进行调整可以释放最佳性能。以下是您需要了解的内容。
以下参数让您可以灵活地根据特定工作负载微调 Qdrant 的性能。您可以直接在 Qdrant 的 配置文件 中修改它们,或在集合和命名向量级别进行更细粒度的控制。
图 3: 三个关键 HNSW 参数的描述。

1. m 参数确定每个节点的边数
这控制了图中边的数量。值越高,搜索准确性越高,但需要更多的内存和构建时间。微调此参数以平衡内存使用和精度。
2. ef_construct 参数控制索引构建范围
此参数设置在索引构建期间考虑的邻居数量。值越大,索引的准确性越高,但会增加构建时间。使用此参数自定义您的索引速度与质量。
在创建集合时,您需要同时设置 m 和 ef 参数。
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 的基础知识。了解更多关于 索引 的信息。
数据压缩技术

高效的数据压缩是向量数据库中资源优化的基石。通过减少内存使用,您可以在不牺牲太多准确性的情况下实现更快的查询性能。
一种强大的技术是 量化,它将高维向量转换为紧凑表示,同时保留相对相似性。让我们探索 Qdrant 中可用的量化选项。
标量量化
标量量化在压缩和性能之间取得了出色的平衡,使其成为大多数用例的首选。
此方法最大限度地减少了用于表示每个向量分量的位数。例如,Qdrant 将 32 位浮点值 (float32) 压缩为 8 位无符号整数 (uint8),将内存使用量惊人地减少了 75%。
图 4: 顶部示例显示一个大小为 40 字节的 float32 向量。将其转换为 int8 格式可将其大小减少四倍,同时保持向量之间近似的相似性关系。与原始表示相比,精度损失对于大多数实际应用来说通常可以忽略不计。

标量量化的好处
| 好处 | 描述 |
|---|---|
| 内存使用将下降 | 压缩将内存使用量减少 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% 的极端值以保持更好的准确性。 |
始终在 RAM 中 | 将量化向量保存在 RAM 中以加快搜索速度。 |
调整这些设置以在您的特定工作负载的精度和效率之间取得适当的平衡。
了解更多关于 标量量化 的信息
二值量化
二进制量化通过将每个向量分量压缩到仅 一个比特,将标量量化提升到新的水平。此方法实现了无与伦比的内存效率和查询速度,将内存使用量减少 32 倍,并将搜索速度提高多达 40 倍。
二进制量化的好处
二进制量化非常适合大规模数据集和兼容的嵌入模型,其中压缩和速度至关重要。
图 5: 此方法实现最大压缩。它将内存使用量减少 32 倍,并将搜索速度提高多达 40 倍。

| 好处 | 描述 |
|---|---|
| 高效相似度计算 | 通过点积比较模拟汉明距离,使其快速有效。 |
| 非常适合高维向量 | 与 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以实现即时访问和更快的查询。
了解更多关于 二进制量化 的信息
扩展数据库

在 Qdrant 等分布式系统中高效管理大型数据集需要智能的数据隔离策略。多租户和分片是帮助您处理大量用户特定数据同时保持性能和可伸缩性的重要工具。
多租户
多租户是一种软件架构,其中多个独立用户(或租户)共享相同的资源或环境。在 Qdrant 中,具有逻辑分区的单个集合通常是多租户用例最有效的设置。
图 5: 每个独立的向量都被分配了一个特定的有效负载,表示它属于哪个租户。这就是大量不同租户可以共享一个 Qdrant 集合的方式。

为什么选择多租户?
- 逻辑隔离:确保每个租户的数据在同一集合中保持独立。
- 最小化开销:与为每个用户维护单独的集合相比,减少资源消耗。
- 可伸缩性:在不影响性能的情况下处理高用户量。
以下是您如何在 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: 用户可以在同一集合中上传和查询与他们相关的分片。区域分片有助于避免跨洲际流量。

示例
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 中实现用户定义分片时,两个关键参数对于实现高效数据分布至关重要
分片键:
分片键决定了数据点如何分布在分片中。例如,使用像
tenant_id这样的键可以让您控制 Qdrant 如何分区数据。添加到集合的每个数据点都将根据此键的值分配给一个分片,从而确保数据的逻辑隔离。分片数量:
这定义了每个分片键的物理分片总数,影响资源分配和查询性能。
以下是您如何将数据点添加到具有用户定义分片的集合中
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 分片键将点分配给特定分片,确保正确的数据放置。
以下是如何选择 shard_number
| 建议 | 描述 |
|---|---|
| 将分片与节点匹配 | 分片数量应与集群中的节点数量对齐,以平衡资源利用率和查询性能。 |
| 规划可扩展性 | 每个节点至少从 2 个分片开始,为未来的增长留出空间。 |
| 面向未来 | 从大约 12 个分片开始是一个很好的经验法则。此设置允许您的系统从 1 个节点无缝扩展到 12 个节点,而无需重新分片。 |
了解更多关于 分布式部署中的分片 的信息
查询优化
在处理大型数据集和复杂查询时,提高向量数据库性能至关重要。通过利用过滤、批处理、重新排序、重新评分和过采样等技术,您可以确保快速响应时间并即使在大规模应用中也能保持效率。
筛选
过滤允许您只选择查询结果中所需的字段。通过限制输出大小,您可以显著减少响应时间并提高性能。
可过滤向量索引通过向搜索图添加专用链接来解决 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: 可过滤向量索引向搜索图添加了专用链接以加快遍历。

可过滤向量索引:此技术在剩余数据点之间构建附加链接(橙色)。留下的过滤点现在可以再次遍历。Qdrant 使用特殊的基于类别的方法来连接这些数据点。
批处理
批处理将多个操作合并到一个执行周期中,减少了请求开销并提高了吞吐量。它是一种有效的数据插入和查询执行策略。

批量插入:不要单独插入向量,而是将它们分组为中等大小的批次,以最大限度地减少数据库请求的数量和频繁写入的开销。
示例
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 中的混合搜索同时使用融合和重新排序。前者是根据每种方法返回的分数来组合来自不同搜索方法的结果。这通常涉及一些归一化,因为不同方法返回的分数可能在不同的范围内。
图 8:混合搜索架构

之后,有一个公式,它采用相关性度量并计算我们稍后用于重新排序文档的最终分数。Qdrant 内置支持 Reciprocal Rank Fusion 方法,这是该领域的事实标准。
了解更多关于 混合搜索 的信息,并阅读我们的 混合查询文档。
过采样
过采样是一种有助于补偿因量化而导致的任何精度损失的技术。由于量化简化了向量,因此在初始搜索中可能会错过一些相关的匹配项。为了避免这种情况,您可以检索更多候选对象,从而增加最相关向量进入最终结果的机会。
您可以通过设置 oversampling 参数来控制额外候选对象的数量。例如,如果您所需的结果数量 (limit) 为 4,并且您设置了 2 的 oversampling 因子,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
)
了解更多关于 重新评分 的信息。
重新排序
重新排序根据附加标准调整搜索结果的顺序,确保最相关的结果获得优先权。
此方法是关于从不同的搜索方法中获取结果,并根据使用文档内容而不是仅仅分数进行的一些额外处理来重新排序它们。此处理可能依赖于额外的神经网络模型,例如交叉编码器,这在整个数据集上使用效率低下。

这些方法仅在用于较小候选子集时才实际适用,这些候选子集由更快的搜索方法返回。在这种情况下,ColBERT 等后期交互模型效率更高,因为它们可以用于重新排序候选对象,而无需访问集合中的所有文档。
示例
client.query_points(
"collection-name",
prefetch=prefetch, # Previous results
query=late_vectors, # Colbert converted query
using="colbertv2.0",
with_payload=True,
limit=10,
)
了解更多关于 重新排序 的信息。
存储:磁盘与 RAM

| 存储 | 描述 |
|---|---|
| RAM | 对于快速访问常用数据(例如索引向量)至关重要。所需的 RAM 量可以根据您的数据集大小和维度进行估算。例如,存储 100 万个 1024 维的向量大约需要 5.72 GB 的 RAM。 |
| 磁盘 | 适用于访问频率较低的数据,例如有效负载和非关键信息。磁盘支持的存储减少了内存需求,但可能会引入轻微的延迟。 |
哪种磁盘类型?
建议使用 本地 SSD 以获得最佳性能,因为它们提供最快的查询响应时间,延迟最小。虽然网络附加存储也可行,但它通常会引入额外的延迟,从而影响性能,因此在可能的情况下,尤其是对于需要高速随机访问的工作负载,首选本地 SSD。
向量和有效负载的内存管理
随着数据的扩展,有效的资源管理对于降低成本同时确保应用程序保持可靠和高性能至关重要。其中一个关键领域是内存管理。
了解 Qdrant 如何处理内存可以帮助您就扩展向量数据库做出明智的决策。Qdrant 支持两种主要的向量存储方法
1. 内存存储
- 工作原理:所有数据都存储在 RAM 中,为查询和操作提供最快的访问时间。
- 何时使用:此设置非常适合对性能至关重要的应用程序,并且您的 RAM 容量可以容纳所有数据。
- 优点:查询和更新速度最快。
- 限制:随着数据集的增长,RAM 使用可能会成为瓶颈。
2. 内存映射存储
- 工作原理:内存映射存储不是将所有数据加载到内存中,而是将数据文件直接映射到磁盘上的虚拟地址空间。系统的页面缓存处理数据访问,使其效率极高。
- 何时使用:非常适合存储超出可用 RAM 的大型集合,同时在有足够 RAM 时仍能保持接近内存的性能。
- 优点:平衡性能和内存使用,允许您处理比物理 RAM 大的数据集。
- 限制:略慢于纯内存存储,但可扩展性显著提高。
要在 Qdrant 中启用内存映射向量存储,您可以在创建或更新集合时将 on_disk 参数设置为 true。
client.create_collection(
collection_name="{collection_name}",
vectors_config=models.VectorParams(
…
on_disk=True
)
)
对有效负载执行相同操作
client.create_collection(
collection_name="{collection_name}",
on_disk_payload= True
)
Qdrant 中选择存储方法的通用指导原则是,当高优先级是高性能且有足够的 RAM 来容纳数据集时,使用 InMemory 存储。这种方法通过将数据保持在内存中以便随时访问来确保最快的访问速度。
然而,对于大型数据集或内存有限的场景,Memmap 和 OnDisk 存储更适合。这些方法通过将数据存储在磁盘上,同时利用页面缓存和索引等高级技术来保持高效且相对快速的数据访问,从而显著减少内存使用。
监控数据库

持续监控对于维护系统健康并在潜在问题升级之前识别它们至关重要。Prometheus 和 Grafana 等工具被广泛用于实现此目的。
- Prometheus:一个开源的监控和警报工具包,Prometheus 在时间序列数据库中收集和存储指标。它从预定义端点抓取指标,并支持强大的查询和可视化功能。
- Grafana:通常与 Prometheus 搭配使用,Grafana 提供了一个直观的界面,用于可视化指标和创建交互式仪表板。
Qdrant 通过 /metrics 端点以 Prometheus/OpenMetrics 格式公开指标。Prometheus 可以抓取此端点以监控 Qdrant 系统的各个方面。
对于本地 Qdrant 实例,指标端点通常可在以下位置找到
http://localhost:6333/metrics
以下是一些重要的监控指标
| 指标名称 | 含义 | |
|---|---|---|
| collections_total | 集合总数 | |
| collections_vector_total | 所有集合中的向量总数 | |
| rest_responses_avg_duration_seconds | REST API 中平均响应持续时间 | |
| grpc_responses_avg_duration_seconds | gRPC API 中平均响应持续时间 | |
| rest_responses_fail_total | 失败响应总数 (REST) |
阅读更多关于 Qdrant 开源监控 和 Qdrant 云监控 (针对托管集群)的信息。
回顾:何时应该优化?

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

