跳到主要内容

Knowhere

本主题介绍了 Knowhere,Milvus 的核心向量执行引擎。

概述

Knowhere 是 Milvus 的核心向量执行引擎,它集成了几个向量相似性搜索库,包括 FaissHnswlibAnnoy。Knowhere 还设计为支持异构计算。它控制在哪种硬件(CPU 或 GPU)上执行索引构建和搜索请求。这就是 Knowhere 名称的由来——知道在哪里执行操作。未来版本将支持更多类型的硬件,包括 DPU 和 TPU。

Milvus 架构中的 Knowhere

下图说明了 Knowhere 在 Milvus 架构中的位置。

Knowhere

最底层是系统硬件。在其上是第三方索引库。在顶层,Knowhere 通过 CGO 与索引节点和查询节点交互,CGO 允许 Go 包调用 C 代码。

Knowhere 的优势

以下是 Knowhere 相对于 Faiss 的优势。

支持 BitsetView

Milvus 引入了位集机制来实现"软删除"。软删除的向量仍然存在于数据库中,但在向量相似性搜索或查询期间不会被计算。

位集中的每一位对应一个索引向量。如果向量在位集中被标记为"1",则意味着该向量被软删除,不会参与向量搜索。位集参数应用于 Knowhere 中所有暴露的 Faiss 索引查询 API,包括 CPU 和 GPU 索引。

有关位集机制的更多信息,请查看 bitset

支持多种二进制向量索引的相似性度量

Knowhere 支持 HammingJaccardTanimotoSuperstructureSubstructure。Jaccard 和 Tanimoto 可用于测量两个样本集之间的相似性,而 Superstructure 和 Substructure 可用于测量化学结构的相似性。

支持 AVX512 指令集

除了 Faiss 已经支持的指令集 AArch64SSE4.2AVX2 之外,Knowhere 还支持 AVX512,与 AVX2 相比,它可以提高索引构建和查询性能 20% 到 30%

自动 SIMD 指令选择

Knowhere 支持在任何 CPU 处理器(本地和云平台)上自动调用合适的 SIMD 指令(例如,SIMD SSE、AVX、AVX2 和 AVX512),因此用户无需在编译期间手动指定 SIMD 标志(例如"-msse4")。

Knowhere 是通过重构 Faiss 的代码库构建的。依赖 SIMD 加速的通用函数(例如相似性计算)被分解出来。然后为每个函数实现四个版本(即 SSE、AVX、AVX2、AVX512),每个版本都放在单独的源文件中。然后源文件使用相应的 SIMD 标志分别编译。因此,在运行时,Knowhere 可以根据当前 CPU 标志自动选择最适合的 SIMD 指令,然后使用挂钩链接正确的函数指针。

其他性能优化

阅读 Milvus: A Purpose-Built Vector Data Management System 了解更多关于 Knowhere 性能优化的信息。

Knowhere 代码结构

Milvus 中的计算主要涉及向量和标量操作。Knowhere 只处理向量索引上的操作。

索引是独立于原始向量数据的数据结构。通常,索引需要四个步骤:创建索引、训练数据、插入数据和构建索引。在一些 AI 应用程序中,数据集训练与向量搜索分离。来自数据集的数据首先被训练,然后插入到像 Milvus 这样的向量数据库中进行相似性搜索。例如,开放数据集 sift1M 和 sift1B 区分用于训练的数据和用于测试的数据。

但是,在 Knowhere 中,用于训练和搜索的数据是相同的。Knowhere 训练 segment 中的所有数据,然后插入所有训练过的数据并为它们构建索引。

DataObj:基类

DataObj 是 Knowhere 中所有数据结构的基类。Size()DataObj 中唯一的虚方法。Index 类继承自 DataObj,带有名为"size_"的字段。Index 类还有两个虚方法——Serialize()Load()。从 Index 派生的 VecIndex 类是所有向量索引的虚基类。VecIndex 提供包括 Train()Query()GetStatistics()ClearStatistics() 在内的方法。

base class

上图右侧列出了一些其他索引类型。

  • Faiss 索引有两个基类:用于浮点向量上所有索引的 FaissBaseIndex,以及用于二进制向量上所有索引的 FaissBaseBinaryIndex

  • GPUIndex 是所有 Faiss GPU 索引的基类。

  • OffsetBaseIndex 是所有自主开发索引的基类。由于索引文件中只存储向量 ID,128 维向量的文件大小可以减少 2 个数量级。

IDMAP:暴力搜索

IDMAP

从技术上讲,IDMAP 不是索引,而是用于暴力搜索。当向量插入数据库时,既不需要数据训练也不需要索引构建。搜索将直接在插入的向量数据上进行。

但是,为了代码的一致性,IDMAP 也继承自 VecIndex 类及其所有虚接口。IDMAP 的使用与其他索引相同。

IVF 索引

IVF

IVF(倒排文件)索引是最常用的。IVF 类派生自 VecIndexFaissBaseIndex,并进一步扩展为 IVFSQIVFPQGPUIVF 派生自 GPUIndexIVF。然后 GPUIVF 进一步扩展为 GPUIVFSQGPUIVFPQ

IVFSQHybrid 是自主开发的混合索引。粗量化器在 GPU 上执行,而在 CPU 上进行桶内搜索。这种类型的索引可以通过利用 GPU 的计算能力来减少 CPU 和 GPU 之间内存复制的发生。IVFSQHybridGPUIVFSQ 具有相同的召回率,但性能更好。

二进制索引的基类结构相对简单。BinaryIDMAPBinaryIVF 派生自 FaissBaseBinaryIndexVecIndex

第三方索引

third-party indices

目前,除了 Faiss 之外,只支持两种类型的第三方索引:基于树的索引 Annoy 和基于图的索引 HNSW。这两个常见且频繁使用的第三方索引都派生自 VecIndex

向 Knowhere 添加索引

如果您想向 Knowhere 添加新索引,首先可以参考现有索引:

  • 要添加基于量化的索引,请参考 IVF_FLAT

  • 要添加基于图的索引,请参考 HNSW

  • 要添加基于树的索引,请参考 Annoy

参考现有索引后,您可以按照以下步骤向 Knowhere 添加新索引。

  1. IndexEnum 中添加新索引的名称。数据类型是字符串。

  2. 在文件 ConfAdapter.cpp 中为新索引添加数据验证检查。验证检查主要是验证数据训练和查询的参数。

  3. 为新索引创建新文件。新索引的基类应包括 VecIndex,以及 VecIndex 的必要虚接口。

  4. VecIndexFactory::CreateVecIndex() 中为新索引添加索引构建逻辑。

  5. unittest 目录下添加单元测试。

下一步

在了解 Knowhere 在 Milvus 中的工作原理后,您可能还想: