是什么增强了 Milvus 向量数据库的相似性搜索功能?
封面图片
作为核心向量执行引擎,Knowhere之于Milvus,就如同发动机之于跑车。本文将介绍什么是 Knowhere,它与 Faiss 有何不同,以及 Knowhere 的代码结构。
跳转到
Knowhere的概念
狭义地说,Knowhere是一个操作界面,用于访问系统上层的服务和系统下层的向量相似性搜索库(如Faiss、Hnswlib、Annoy)。此外,Knowhere 还负责异构计算。更具体地说,Knowhere控制在哪种硬件(如CPU或GPU)上执行索引构建和搜索请求。这就是 Knowhere 名字的由来--知道在哪里执行操作符。未来的版本将支持更多类型的硬件,包括DPU和TPU。
从广义上讲,Knowhere还集成了其他第三方索引库,如Faiss。因此,从整体上看,Knowhere 是公认的 Milvus 向量数据库的核心向量计算引擎。
从Knowhere的概念可以看出,它只处理数据计算任务,而分片、负载均衡、灾难恢复等任务则超出了Knowhere的工作范围。
从Milvus 2.0.1开始,Knowhere(广义上的)从Milvus项目中独立出来。
Milvus 架构中的 Knowhere
Knowhere 架构
Milvus 中的计算主要涉及向量和标量操作。Knowhere 只处理 Milvus 中对向量的操作符。上图展示了 Milvus 中的 Knowhere 架构。
最底层是系统硬件。第三方索引库位于硬件之上。然后,Knowhere通过CGO与顶层的索引节点和查询节点交互。
本文所讨论的Knowhere是广义上的Knowhere,即架构图中蓝色框内所标注的Knowhere。
Knowhere 与 Faiss
Knowhere不仅进一步扩展了Faiss的功能,还优化了性能。更具体地说,Knowhere 具有以下优势。
1.支持比特视图
最初,在 Milvus 中引入 bitset 是为了实现 "软删除"。软删除的向量仍然存在于数据库中,但在向量相似性搜索或查询时不会被计算。比特集中的每个比特都对应一个索引向量。如果某个向量在比特集中被标记为 "1",则表示该向量已被软删除,在向量搜索过程中将不会涉及。
比特集参数被添加到 Knowhere 中所有公开的 Faiss 索引查询 API 中,包括 CPU 和 GPU 索引。
进一步了解bitset 如何实现向量搜索的多功能性。
2.支持更多二进制向量索引的相似度指标
除Hamming 外,Knowhere 还支持Jaccard、Tanimoto、Superstructure 和Substructure。Jaccard 和 Tanimoto 可用于测量两个样本集之间的相似性,而 Superstructure 和 Substructure 可用于测量化学结构的相似性。
3.支持 AVX512 指令集
Faiss 本身支持多种指令集,包括AArch64、SSE4.2 和AVX2。Knowhere 通过添加AVX512 进一步扩展了支持的指令集,与 AVX2 相比,AVX512 可将索引构建和查询性能提高 20% 至 30%。
4.自动选择 SIMD 指令
Knowhere 的设计目标是在具有不同 SIMD 指令(如 SIMD SSE、AVX、AVX2 和 AVX512)的各种 CPU 处理器(本地部署和云平台)上良好运行。因此,面临的挑战是,给定一个软件二进制文件(即 Milvus),如何让它在任何 CPU 处理器上自动调用合适的 SIMD 指令?Faiss 不支持自动选择 SIMD 指令,用户需要在编译时手动指定 SIMD 标志(如"-msse4")。不过,Knowhere 是通过重构 Faiss 的代码库而构建的。依赖 SIMD 加速的常用函数(如相似性计算)被分解出来。然后为每个函数实现四个版本(即 SSE、AVX、AVX2、AVX512),并将每个版本放入单独的源文件中。然后,使用相应的 SIMD 标志对源文件进行单独编译。因此,在运行时,Knowhere 可以根据当前的 CPU 标志自动选择最合适的 SIMD 指令,然后使用挂钩功能链接正确的函数指针。
5.其他性能优化
阅读《Milvus: A Purpose-Built Vector Data Management System》,了解有关 Knowhere 性能优化的更多信息。
了解 Knowhere 代码
如第一节所述,Knowhere 只处理向量搜索操作。因此,Knowhere只处理实体的向量场(目前只支持一个 Collections 中实体的一个向量场)。索引建立和向量相似性搜索也是针对段中的向量场。要想更好地了解数据模型,请阅读此处的博客。
实体字段
索引
索引是一种独立于原始向量数据的数据结构。索引需要四个步骤:创建索引、训练数据、插入数据和建立索引。
对于某些人工智能应用来说,数据集训练是一个独立于向量搜索的过程。在这类应用中,首先要对数据集的数据进行训练,然后将数据插入到 Milvus 这样的向量数据库中进行相似性搜索。开放数据集(如 sift1M 和 sift1B)为训练和测试提供了数据。然而,在 Knowhere 中,训练数据和搜索数据是混合在一起的。也就是说,Knowhere 会训练一个数据段中的所有数据,然后插入所有训练过的数据并为它们建立索引。
Knowhere 代码结构
DataObj 是 Knowhere 中所有数据结构的基类。Size()
是 DataObj 中唯一的虚拟方法。Index 类继承自 DataObj,并带有一个名为 "size_"的字段。Index 类还有两个虚拟方法--Serialize()
和Load()
。从 Index 派生的 VecIndex 类是所有向量索引的虚拟基类。VecIndex 提供的方法包括Train()
,Query()
,GetStatistics()
和ClearStatistics()
。
基类
上图右侧列出了其他索引类型。
- Faiss 索引有两个子类:FaissBaseIndex 用于浮点向量上的所有索引,FaissBaseBinaryIndex 用于二进制向量上的所有索引。
- GPUIndex 是所有 Faiss GPU 索引的基类。
- OffsetBaseIndex 是所有自主开发索引的基类。索引文件中只存储向量 ID。因此,128 维向量的索引文件大小可以减少 2 个数量级。我们建议在使用这类索引进行向量相似性搜索时,也将原始向量考虑在内。
IDMAP
从技术上讲,IDMAP不是索引,而是用于暴力搜索。当向量被插入到向量数据库时,不需要进行数据训练和建立索引。搜索将直接在插入的向量数据上进行。
不过,为了代码的一致性,IDMAP 也继承自 VecIndex 类及其所有虚拟接口。IDMAP 的用法与其他索引相同。
反转 文件
IVF(倒置文件)索引是最常用的索引。IVF 类派生自 VecIndex 和 FaissBaseIndex,并进一步扩展到 IVFSQ 和 IVFPQ。GPUIVF 衍生自 GPUIndex 和 IVF。然后 GPUIVF 进一步扩展到 GPUIVFSQ 和 GPUIVFPQ。
IVFSQHybrid 是一个自主开发的混合索引类,它在 GPU 上通过粗量化执行。而桶中的搜索则在 CPU 上执行。这种索引可以利用 GPU 的计算能力,减少 CPU 和 GPU 之间的内存拷贝。IVFSQHybrid 的召回率与 GPUIVFSQ 相同,但性能更好。
二进制索引的基类结构相对更简单。BinaryIDMAP 和 BinaryIVF 源自 FaissBaseBinaryIndex 和 VecIndex。
第三方索引
目前,除了 Faiss 之外,只支持两种第三方索引:基于树的索引 Annoy 和基于图的索引 HNSW。这两种常用的第三方索引都源自 VecIndex。
向Knowhere添加索引
如果想在Knowhere中添加新的索引,可以先参考现有的索引:
- 要添加基于量化的索引,请参考 IVF_FLAT。
- 要添加基于图形的索引,请参考 HNSW。
- 要添加基于树的索引,请参考 Annoy。
参考现有索引后,可以按照以下步骤向 Knowhere 添加新索引。
- 在
IndexEnum
中添加新索引的名称。数据类型为字符串。 - 在文件
ConfAdapter.cpp
中为新索引添加数据验证检查。验证检查主要用于验证数据训练和查询的参数。 - 为新索引创建一个新文件。新索引的基类应包括
VecIndex
和VecIndex
的必要虚拟接口。 - 在
VecIndexFactory::CreateVecIndex()
中添加新索引的索引构建逻辑。 - 在
unittest
目录下添加单元测试。
关于深入研究系列
随着 Milvus 2.0正式宣布全面上市,我们精心策划了这个 Milvus 深度挖掘系列博客,对 Milvus 架构和源代码进行深入解读。本系列博客涉及的主题包括
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word