优化批量上传期间的内存消耗
在处理大规模向量数据时,高效的内存管理始终是一个挑战。在高吞吐量数据摄取场景中,即使看似微小的配置选择也可能显著影响系统的稳定性和性能。
本文将探讨最佳实践和建议,帮助您优化 Qdrant 批量上传期间的内存使用。我们将介绍密集向量和稀疏向量的场景,帮助您的部署在高负载下保持高性能,并避免内存不足错误。
密集向量与稀疏向量的索引
密集向量
Qdrant 使用基于 HNSW 的索引来对密集向量进行快速相似度搜索。默认情况下,一旦段中未索引向量的数量超过设定的 indexing_threshold
,就会构建或更新 HNSW。虽然它提供了出色的查询速度,但如果 HNSW 图频繁构建或跨许多小段构建,则可能消耗大量资源。
稀疏向量
稀疏向量使用倒排索引。此索引在插入更新时更新,这意味着对于稀疏向量,您无法禁用或延迟它。在大多数情况下,其开销小于构建 HNSW 图的开销,但您仍应注意每次插入更新都会触发稀疏索引更新。
密集向量的批量上传配置
执行高吞吐量向量摄取时,您有两个主要选项来处理索引开销。您应该根据您的具体工作负载和内存限制选择其中一个
- 禁用 HNSW 索引
为了在批量摄取期间减少内存和 CPU 压力,您可以通过设置 "m": 0
来完全禁用 HNSW 索引。对于密集向量,m
参数定义了 HNSW 图中每个节点可以拥有的边数。这样,将不会构建密集向量索引,从而避免在摄取期间不必要的 CPU 使用。
图 1:三个关键 HNSW 参数的描述。

PATCH /collections/your_collection
{
"hnsw_config": {
"m": 0
}
}
摄取完成后,您可以通过将 m
设置回生产值(通常为 16 或 32)来重新启用 HNSW。请记住,在索引构建完成之前,搜索不会使用 HNSW,因此在此期间搜索性能可能会较慢。
- 完全禁用优化
indexing_threshold
告诉 Qdrant 在构建 HNSW 图之前,一个段中可以累积多少个未索引的密集向量。将 "indexing_threshold"=0
会完全延迟索引,使摄取速度达到最大。但是,这意味着上传的向量在上传期间不会移动到磁盘,这可能导致高 RAM 使用率。
PATCH /collections/your_collection
{
"optimizer_config": {
"indexing_threshold": 0
}
}
批量摄取后,将 indexing_threshold
设置为正值,以确保向量通过 HNSW 进行索引和搜索。在执行索引之前,向量将无法通过 HNSW 进行搜索。
较小的阈值(例如 100)意味着更频繁的索引,如果存在许多段,这仍然可能开销很大。较大的阈值(例如 10000)会延迟索引,以便一次批量处理更多向量,在构建索引时可能会占用更多 RAM,但总的构建次数会减少。
在这两种方法中,我们通常建议在批量摄取期间禁用 HNSW (`"m"=0`),以保持内存使用可预测。使用 indexing_threshold=0
可以作为替代方案,但前提是您的系统有足够的内存来容纳 RAM 中的未索引向量。
Qdrant 中的磁盘存储
默认情况下,Qdrant 将向量、载荷数据和索引保存在内存中,以确保低延迟查询。然而,在大规模或内存受限的场景中,您可以配置将其中部分或全部数据存储在磁盘上。这有助于降低 RAM 使用,但可能会增加查询延迟,特别是对于冷读取。
何时使用磁盘存储:
- 您有非常大或很少使用的载荷数据或索引,并且释放 RAM 值得承担潜在的 I/O 开销。
- 您的数据集无法舒适地容纳在可用内存中。
- 您想减轻内存压力。
- 如果可以确保系统在高负载下保持稳定,您可以容忍较慢的查询。
内存映射存储和分段
Qdrant 使用内存映射文件(段)将数据存储在磁盘上。Qdrant 不会将所有向量加载到 RAM 中,而是将每个段映射到其地址空间,按需将数据分页进出。这有助于降低活动 RAM 占用空间,因为在内存压力高时可以将数据分页出去。但每个段仍然会产生开销(元数据、页表条目等)。
在高吞吐量摄取期间,您可能会累积数十个小段。Qdrant 的优化器稍后可以将这些小段合并成更少、更大的段,从而减少每个段的开销并降低总内存使用量。
当您使用 "on_disk": true
创建集合时,Qdrant 将从一开始就将新插入的向量存储在内存映射存储中。例如
PATCH /collections/your_collection
{
"vectors": {
"on_disk": true
}
}
这种方法立即将所有传入向量放在磁盘上,这在批量摄取的情况下效率很高。
然而,向量数据和索引是分开存储的,因此为向量启用 on_disk
不会自动将其索引存储在磁盘上。为了完全优化内存使用,您可能需要独立配置向量存储和索引存储。
对于密集向量,您可以为向量数据和 HNSW 索引启用磁盘存储
PATCH /collections/your_collection
{
"vectors": {
"on_disk": true
},
"hnsw_config": {
"on_disk": true
}
}
对于稀疏向量,您需要分别对向量数据和稀疏索引启用 on_disk
PATCH /collections/your_collection
{
"sparse_vectors": {
"text": {
"on_disk": true,
"index": {
"on_disk": true
}
}
}
}
高吞吐量向量摄取的最佳实践
批量摄取可能导致高内存消耗,甚至内存不足 (OOM) 错误。如果您的当前设置出现内存不足错误,临时向上扩展(增加可用 RAM)将提供缓冲,同时您可以调整 Qdrant 的配置以实现更高效的数据摄取。
这里的关键是控制索引开销。让我们来看看在内存受限环境中进行高吞吐量向量摄取的最佳实践。
1. 立即将向量数据存储在磁盘上
减少内存使用最有效的方法是使用 on_disk: true
从一开始就将向量数据存储在磁盘上。这可以防止 RAM 在优化生效前被原始向量过载。
PATCH /collections/your_collection
{
"vectors": {
"on_disk": true
}
}
以前,向量数据必须保存在 RAM 中,直到优化器将其移动到磁盘,这导致了显著的内存压力。现在,通过直接将向量写入磁盘,内存开销显著降低,使得批量摄取更加高效。
2. 禁用密集向量的 HNSW (`m=0`)
在初次批量加载期间,您可以通过设置 "m": 0
来禁用密集索引。这确保 Qdrant 不会为传入向量构建 HNSW 图,从而避免不必要的内存和 CPU 使用。
PATCH /collections/your_collection
{
"hnsw_config": {
"m": 0
},
"optimizer_config": {
"indexing_threshold": 10000
}
}
3. 在批量上传之后运行优化器
Qdrant 的优化器会持续重构数据以提高搜索效率。然而,在批量上传期间,这可能导致过多的数据移动和开销,因为在新数据仍在到达的同时,段不断地被重组。
为避免这种情况,请先上传所有数据,然后让优化器一次性处理所有内容。这最大限度地减少了冗余操作,并确保了更有效的段结构。
4. 等待索引完成以释放内存
在执行其他操作之前,请等待 Qdrant 完成所有正在进行的索引任务。大型索引作业可能会使内存使用率保持较高水平,直到完全完成。
监控 Qdrant 日志或指标以确认索引何时完成——一旦完成,随着中间数据结构的释放,内存消耗应该会下降。
5. 摄取后重新启用 HNSW
摄取阶段完成后,且内存使用稳定后,通过将 m
设置回生产值(通常为 16
或 32
)来重新启用密集向量的 HNSW
PATCH /collections/your_collection
{
"hnsw_config": {
"m": 16
}
}
5. 启用量化
如果您曾计划将所有密集向量存储在磁盘上,请注意,在内存压力高时,由于频繁的磁盘 I/O,搜索速度可能会急剧下降。一个更平衡的方法是标量量化:压缩向量(例如,到 int8
),以便它们可以放入 RAM 中,而不会占用像完整的浮点值那样多的空间。
PATCH /collections/your_collection
{
"quantization_config": {
"scalar": {
"type": "int8",
"always_ram": true
}
}
}
量化向量保持在内存中,但占用空间较少,保留了基于 RAM 的搜索的大部分性能优势。了解更多关于向量量化的信息。
结论
高吞吐量向量摄取可能对 Qdrant 施加显著的内存需求,特别是当密集向量实时索引时。通过遵循这些技巧,您可以大大降低内存不足错误的风险,并在内存受限的环境中保持稳定的性能。
一如既往,监控您的系统行为。检查日志,查看指标,并密切关注内存使用情况。每个工作负载都不同,因此根据您的硬件和数据规模微调 Qdrant 的参数是明智的。