如何通过Qdrant数据库优化大规模相似性搜索和推荐系统?

在当今数据驱动的时代,推荐系统和相似性搜索对于提升用户体验和商业价值起着至关重要的作用。无论是在电商平台上推荐产品,还是在社交媒体中定制内容,背后的技术都依赖于强大的数据处理能力。在这一背景下,Qdrant向量数据库脱颖而出,成为处理高维向量数据的关键工具。本文将详细探讨Qdrant的独特功能、应用场景以及其在推荐系统和相似性搜索中的实际应用,帮助技术人员和开发者更好地理解如何利用这一工具提升系统性能。

一、什么是Qdrant向量数据库?

Qdrant 是一个开源的向量数据库,专为高效的相似性搜索和推荐系统而设计。它允许存储和管理高维向量数据,例如文本、图像和音频的嵌入,并能够进行快速的最近邻搜索(ANN)。Qdrant 的核心特性在于其动态过滤功能和强大的查询规划能力,这使其在处理大型数据集时依然能保持高效和准确的搜索体验。

Qdrant的核心功能亮点

  • 向量搜索:Qdrant 支持高效的近似最近邻(ANN)搜索,能够快速找到与查询向量最相似的结果。
  • 动态过滤:与大多数其他 ANN 算法不同,Qdrant 可以在搜索时应用过滤条件,而无需在搜索前后进行过滤,这提升了查询的精确性和效率。
  • HNSW 索引:Qdrant 使用 HNSW(Hierarchical Navigable Small World)算法,这是一种高效的图索引结构,能够进行快速的向量搜索。
  • 查询规划:Qdrant 的查询规划根据索引类型、过滤条件的复杂性和过滤结果的基数自动选择最优的查询执行策略,从而确保性能最佳化。
  • 支持嵌入和推荐系统:Qdrant 是为处理深度学习生成的嵌入(如自然语言处理模型或图像模型的向量表示)而设计的,适用于构建推荐系统、个性化搜索等场景。
  • 水平扩展和高可用性:Qdrant 设计支持水平扩展,能够处理大规模的数据集,并且具有良好的可用性,适合生产环境。

Qdrant的应用场景

1. 推荐系统:基于用户的行为向量找到与其偏好最匹配的商品或内容

推荐系统广泛应用于电商、内容平台和社交媒体中。Qdrant能够根据用户行为生成的向量数据,为用户实时推荐最相关的商品或内容。例如,在电商中,Qdrant可以帮助推荐与用户之前浏览或购买的商品相似的产品,提升购物体验。

2. 图像和文本搜索:使用深度学习模型生成的嵌入,快速检索与查询最相似的图像或文本

借助深度学习模型生成的嵌入,Qdrant可以快速检索与查询向量最相似的图像或文本。这一功能在视觉搜索和自然语言处理应用中尤为重要,例如通过图像搜索相似商品或通过文本嵌入搜索相关内容。

3. 个性化内容推荐:根据用户的兴趣和偏好实时推荐相关内容

通过实时分析用户的兴趣和偏好,Qdrant 可以根据用户向量提供实时的个性化内容推荐。这在资讯平台、社交媒体等需要实时反馈的场景中,能够极大提升用户的参与度和留存率。

二、HNSW索引:Qdrant快速搜索的核心

在Qdrant中,HNSW索引是一种能够快速搜索的关键算法。它通过构建图结构,使得向量搜索的效率大幅提升。以下是HNSW的几个核心参数及其重要性:

  • M:每个节点在索引图中的边数。值越大,搜索越准确,但所需的空间也越多。 这个参数定义了 HNSW 索引中每个节点连接的邻居节点的数量。增加 M 的值可以提高搜索精度,因为节点连接更多的邻居。然而,较大的 M 也意味着需要更多的存储空间。
  • EfConstruct:索引构建时考虑的邻居数。值越大,搜索越准确,但构建索引所需的时间也越长。 在构建 HNSW 索引时,EfConstruct 参数决定了在构建过程中考虑的最大候选邻居数。更大的 EfConstruct 值会使得索引更精确,但也会增加构建索引所需的时间。
  • FullScanThreshold:用于额外基于负载的索引的向量的最小大小(以千字节为单位)。如果负载块小于 full_scan_threshold,则不会使用额外的索引;在这种情况下,查询规划器应该优先考虑全扫描搜索,不需要额外的索引。这个参数定义了一个阈值,用于决定是否进行额外的负载索引。如果向量的大小小于该阈值,系统会选择全扫描搜索而不是进行额外的索引。举例来说,1 KB 相当于一个 256 维的向量。
  • MaxIndexingThreads: 用于后台索引构建的并行线程数。如果设置为 0,则会自动选择 8 到 16 个线程。最好保持在 8 到 16 之间,以防构建出有缺陷或效率低下的 HNSW 图。在小型 CPU 上,使用的线程数会更少。 这个参数决定了用于索引构建的并行线程数。如果设置为 0,系统会根据硬件自动选择线程数。较少的线程数可能会导致索引构建速度变慢或生成的索引不够高效。
  • OnDisk:将 HNSW 索引存储在磁盘上。如果设置为 false,索引将存储在 RAM 中。这个参数决定了 HNSW 索引的存储位置。设置为 true 表示将索引存储在磁盘上,而不是内存中。存储在磁盘上的索引可以处理更大的数据集,但访问速度可能较慢。
  • PayloadM:每个节点在索引图中额外的基于负载的链接数。如果未设置,则使用常规的 M 参数。这个参数指定了每个节点的额外负载链接数,这些链接是为了提高与负载相关的数据的处理效率。如果没有设置此参数,则使用默认的 M 参数值。

为了在不同场景中获得最佳性能,合理配置HNSW的参数尤为重要。对于实时推荐系统,需要在速度和精度之间找到最佳平衡。通过调整M、EfConstruct等参数,系统可以根据不同的数据规模和搜索需求进行优化。

- 可配置核心参数

// Number of edges per node in the index graph. Larger the value - more accurate the search, more space required.
M *uint64 `protobuf:"varint,1,opt,name=m,proto3,oneof" json:"m,omitempty"`
// Number of neighbours to consider during the index building. Larger the value - more accurate the search, more time required to build the index.
EfConstruct *uint64 `protobuf:"varint,2,opt,name=ef_construct,json=efConstruct,proto3,oneof" json:"ef_construct,omitempty"`
// Minimal size (in KiloBytes) of vectors for additional payload-based indexing.
// If the payload chunk is smaller than `full_scan_threshold` additional indexing won't be used -
// in this case full-scan search should be preferred by query planner and additional indexing is not required.
// Note: 1 Kb = 1 vector of size 256
FullScanThreshold *uint64 `protobuf:"varint,3,opt,name=full_scan_threshold,json=fullScanThreshold,proto3,oneof" json:"full_scan_threshold,omitempty"`
// Number of parallel threads used for background index building.
// If 0 - automatically select from 8 to 16.
// Best to keep between 8 and 16 to prevent likelihood of building broken/inefficient HNSW graphs.
// On small CPUs, less threads are used.
MaxIndexingThreads *uint64 `protobuf:"varint,4,opt,name=max_indexing_threads,json=maxIndexingThreads,proto3,oneof" json:"max_indexing_threads,omitempty"`
// Store HNSW index on disk. If set to false, the index will be stored in RAM.
OnDisk *bool `protobuf:"varint,5,opt,name=on_disk,json=onDisk,proto3,oneof" json:"on_disk,omitempty"`
// Number of additional payload-aware links per node in the index graph. If not set - regular M parameter will be used.
PayloadM *uint64 `protobuf:"varint,6,opt,name=payload_m,json=payloadM,proto3,oneof" json:"payload_m,omitempty"`

三、优化器核心参数:提升性能的关键

Qdrant的优化器功能帮助管理和压缩数据,以确保系统在处理大数据集时仍能保持高效。以下是几个优化器核心参数:

  • DeletedThreshold:执行段优化所需的段中删除向量的最小比例。这个参数定义了在进行段优化之前,段中被删除的向量必须占据的最小比例。例如,如果 DeletedThreshold 设置为 0.2,则当段中被删除的向量占总向量的 20% 时,就会触发优化。这个值用于确保只有在段中删除了足够多的向量后才进行优化,以提高优化的效率。
  • VacuumMinVectorNumber:执行段优化所需的段中的最小向量数量。这个参数定义了段中最小的向量数量,只有当段中的向量数量大于或等于这个值时,才会进行优化。这个值确保了优化操作只在有足够数量的向量的情况下进行,以避免对小段进行不必要的优化。
  • DefaultSegmentNumber:优化器尝试保持的目标段数量。这个参数指定了优化器希望保持的默认段数量。实际的段数量可能会根据存储的点数和当前的写入速率(RPS)而有所不同。推荐选择段数量作为搜索线程数的倍数,以确保每个线程能均匀地处理一个段。这样可以提高处理效率。
  • MaxSegmentSize:不创建大于此大小(以千字节为单位)的段。这个参数限制了单个段的最大大小。过大的段可能需要较长的索引时间,因此限制段的大小可以减少索引时间。如果你更关注索引速度,可以将这个参数设置得较低;如果你更关注搜索速度,可以将其设置得较高。默认情况下,未设置时,系统会根据可用的 CPU 数量自动选择一个合理的值。
  • MemmapThreshold:每个段允许的最大向量大小(以千字节为单位)以便存储在内存中。这个参数定义了一个阈值,超过该阈值的段将作为只读的内存映射文件存储。如果段的大小超过这个阈值,则它们将不会完全保存在内存中,而是以只读的方式映射到磁盘。如果你想启用内存映射存储,可以将这个阈值设置为一个合理的值;如果要禁用内存映射存储,可以将其设置为 0。
  • IndexingThreshold:允许的平面索引的最大向量大小,超过此阈值将启用向量索引。这个参数定义了向量的大小阈值。超过这个阈值的向量将使用向量索引,而不是平面索引。默认值为 20,000 千字节,基于 Google Research 的文档。如果想禁用向量索引,可以将其设置为 0。
  • FlushIntervalSec:强制刷新之间的间隔(以秒为单位)。这个参数定义了强制刷新操作之间的时间间隔。系统将在指定的秒数后自动将数据刷新到磁盘,以确保数据的一致性和持久性。
  • MaxOptimizationThreads:每个分片运行优化的最大线程数(作业数)。这个参数定义了每个分片上运行优化操作的最大线程数。每个优化作业将使用 max_indexing_threads 参数指定的线程数进行索引构建。如果设置为 null,系统会动态选择线程数以充分利用 CPU 资源;如果设置为 0,优化线程将被禁用,优化操作也会被禁用。
- 可配置核心参数

// The minimal fraction of deleted vectors in a segment, required to perform segment optimization
DeletedThreshold *float64 `protobuf:"fixed64,1,opt,name=deleted_threshold,json=deletedThreshold,proto3,oneof" json:"deleted_threshold,omitempty"`
// The minimal number of vectors in a segment, required to perform segment optimization
VacuumMinVectorNumber *uint64 `protobuf:"varint,2,opt,name=vacuum_min_vector_number,json=vacuumMinVectorNumber,proto3,oneof" json:"vacuum_min_vector_number,omitempty"`
// Target amount of segments the optimizer will try to keep.
// Real amount of segments may vary depending on multiple parameters:
//
// - Amount of stored points.
// - Current write RPS.
//
// It is recommended to select the default number of segments as a factor of the number of search threads,
// so that each segment would be handled evenly by one of the threads.
DefaultSegmentNumber *uint64 `protobuf:"varint,3,opt,name=default_segment_number,json=defaultSegmentNumber,proto3,oneof" json:"default_segment_number,omitempty"`
// Do not create segments larger this size (in kilobytes).
// Large segments might require disproportionately long indexation times,
// therefore it makes sense to limit the size of segments.
//
// If indexing speed is more important - make this parameter lower.
// If search speed is more important - make this parameter higher.
// Note: 1Kb = 1 vector of size 256
// If not set, will be automatically selected considering the number of available CPUs.
MaxSegmentSize *uint64 `protobuf:"varint,4,opt,name=max_segment_size,json=maxSegmentSize,proto3,oneof" json:"max_segment_size,omitempty"`
// Maximum size (in kilobytes) of vectors to store in-memory per segment.
// Segments larger than this threshold will be stored as read-only memmaped file.
//
// Memmap storage is disabled by default, to enable it, set this threshold to a reasonable value.
//
// To disable memmap storage, set this to `0`.
//
// Note: 1Kb = 1 vector of size 256
MemmapThreshold *uint64 `protobuf:"varint,5,opt,name=memmap_threshold,json=memmapThreshold,proto3,oneof" json:"memmap_threshold,omitempty"`
// Maximum size (in kilobytes) of vectors allowed for plain index, exceeding this threshold will enable vector indexing
//
// Default value is 20,000, based on <https://github.com/google-research/google-research/blob/master/scann/docs/algorithms.md>.
//
// To disable vector indexing, set to `0`.
//
// Note: 1kB = 1 vector of size 256.
IndexingThreshold *uint64 `protobuf:"varint,6,opt,name=indexing_threshold,json=indexingThreshold,proto3,oneof" json:"indexing_threshold,omitempty"`
// Interval between forced flushes.
FlushIntervalSec *uint64 `protobuf:"varint,7,opt,name=flush_interval_sec,json=flushIntervalSec,proto3,oneof" json:"flush_interval_sec,omitempty"`
// Max number of threads (jobs) for running optimizations per shard.
// Note: each optimization job will also use `max_indexing_threads` threads by itself for index building.
// If null - have no limit and choose dynamically to saturate CPU.
// If 0 - no optimization threads, optimizations will be disabled.
MaxOptimizationThreads *uint64 `protobuf:"varint,8,opt,name=max_optimization_threads,json=maxOptimizationThreads,proto3,oneof" json:"max_optimization_threads,omitempty"`

四、如何利用Qdrant优化推荐系统的性能?

Qdrant的独特功能使其成为构建高效推荐系统的理想工具。通过合理配置索引参数、优化数据存储结构以及动态应用过滤条件,系统能够在大规模数据集下依然保持高效的相似性搜索和推荐能力。对于需要处理大数据集、提供实时响应的应用场景,Qdrant提供了极具竞争力的解决方案。

总结

Qdrant向量数据库凭借其高效的向量搜索、动态过滤功能以及强大的查询规划能力,成为了处理大规模相似性搜索和推荐系统的有力工具。无论是电商平台的个性化推荐,还是图像搜索应用,Qdrant都能提供卓越的性能和灵活性。通过优化其核心参数,开发者能够最大限度地发挥其潜力,为用户带来更智能、更精准的搜索和推荐体验。

延展阅读:

如何高效设计软件开发测试用例:解锁关键要点与实战技巧的疑问解答

MongoDB 4.0至7.0:这些主版本更新带来了哪些关键特性与性能飞跃?

电商平台客服工作台频繁卡顿怎么办?如何清理淘宝、京东、拼多多、抖音的缓存提升效率?

咨询方案 获取更多方案详情                        
(0)
研发专家-柯西研发专家-柯西
上一篇 2024年9月11日 上午11:17
下一篇 2024年9月11日 下午6:39

相关推荐