Skip to content

向量检索引擎:Annoy、Faiss、Milvus与ElasticSearch

内容整理自学习笔记,仅供面试备考参考;不构成录用、培训或考试承诺。

1. Annoy

GitHub: https://github.com/spotify/annoy

1.1 Annoy 介绍

Annoy 是高维空间求近似最近邻的一个开源库,全称 Approximate Nearest Neighbors Oh Yeah,是一种适合实际应用的快速相似查找算法。Annoy 构建一个二叉树,查询时间为 O(log n)。

1.2 Annoy 使用

安装:pip install annoy

python
from annoy import AnnoyIndex
import random

# 向量的维度
f = 40

# 返回一个可读可写的存储 f 维向量的索引
t = AnnoyIndex(f, 'angular')
for i in range(1000):
    # random.gauss 为随机生成高斯分布的随机数
    v = [random.gauss(0, 1) for z in range(f)]
    # 在位置 i 添加向量
    t.add_item(i, v)

# 建立一棵 n_trees 的森林,树越多,精度越高
t.build(10)  # 10 trees

# 保存
t.save('test.ann')

# ...
u = AnnoyIndex(f, 'angular')
# 直接加载
u.load('test.ann')  # super fast, will just mmap the file
print(u.get_nns_by_item(0, 1000))  # 找到 1000 个最近邻

1.3 向量检索函数

函数说明
get_nns_by_item(i, n, search_k=-1, include_distances=False)返回最接近 item i 的 n 个 item;include_distances=True 时返回元组(items 列表 + distances 列表)
get_nns_by_vector(v, n, search_k=-1, include_distances=False)给定查询向量 v,返回 n 个最近邻的 item,常用于给定用户 embedding 查询
get_item_vector(i)返回索引 i 对应的向量
get_distance(i, j)返回 item_i 和 item_j 的平方距离
get_n_items()返回索引中的 items 个数(词典大小)
get_n_trees()返回索引树的个数

1.4 关键调参参数

参数说明影响
n_trees构建期间提供值越大结果越准确,但索引越大,影响构建时间和索引大小
search_k运行时提供值越大结果越准确,但返回时间越长。默认为 n_trees * n

提示search_k 实现了准确性和速度之间的运行时权衡,可根据实际场景调整。

2. Faiss

GitHub: https://github.com/facebookresearch/faiss
Tutorial: https://github.com/facebookresearch/faiss/wiki/Getting-started

2.1 Faiss 介绍

Faiss 是由 Facebook 开发的适用于稠密向量匹配的开源库,支持 C++ 与 Python 调用,是向量化检索的开山鼻祖应用。支持多种向量检索方式,包括内积、欧氏距离等,同时支持精确检索与模糊搜索。

2.2 Faiss 主要特性

特性说明
相似度检索和聚类支持多种相似度计算方式
多种索引方式灵活选择索引类型
CPU 和 GPU 计算支持 GPU 加速
Python 和 C++ 调用多语言支持

2.3 Faiss 使用

安装:pip install faiss-cpu --no-cache

使用方法归纳为三个步骤:

python
import numpy as np
import faiss

d = 64       # 向量维度
nb = 100000  # 向量库大小
nq = 10000   # 查询向量数量

# 步骤1:构建向量库
xb = np.random.random((nb, d)).astype('float32')
xb[:, 0] += np.arange(nb) / 1000.
xq = np.random.random((nq, d)).astype('float32')
xq[:, 0] += np.arange(nq) / 1000.

# 步骤2:选择合适的 index,将向量 add 到 index
index = faiss.IndexFlatL2(d)
index.add(xb)

# 步骤3:search 得到最终结果
k = 4
D, I = index.search(xq[:5], k)  # 分别返回距离和索引

三步法:① 构建向量库(矩阵形式)→ ② 选择 index 并 add → ③ search 检索

3. Milvus

更多介绍:https://gitee.com/milvus-io/mil

Milvus 是一款开源的特征向量相似度搜索引擎,使用方便、实用可靠、易于扩展、稳定高效和搜索迅速。

特性说明
高性能涵盖 Faiss、Annoy、hnswlib 等主流第三方索引库,支持海量向量相似搜索
高可用高可靠支持 Kubernetes 部署和云上扩展,容灾能力保证服务高可用
云原生架构使用 Pulsar、Kafka 等消息队列实现组件间通信解耦
混合查询支持向量检索过程中进行标量字段过滤
开发者友好支持 Python、Java、Go、Node.js,提供 Attu 等工具

4. ElasticSearch

4.1 ElasticSearch 介绍

Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,建立在全文搜索引擎 Apache Lucene 基础之上。它不仅是全文搜索功能,还具备以下能力:

能力说明
分布式实时文件存储每一个字段都编入索引,可被搜索
实时分析分布式搜索引擎
海量数据处理可扩展到上百台服务器,处理 PB 级结构化或非结构化数据

ES 本质上是一个支持全文搜索的分布式内存数据库,特别适用于内容检索、文本检索、日志检索。其核心原因是采用了倒排索引

4.2 什么是倒排索引?

倒排索引是一种特别为搜索而设计的索引结构:

  1. 先对需要索引的字段进行分词
  2. 然后以分词为索引组成一个查找树
  3. 这样就把一个全文匹配的查找转换成了对树的查找
对比倒排索引B树索引
适用场景全文搜索交易类数据
查询性能全文搜索快等值/范围查询快
写入更新性能较差性能较好

注意:倒排索引的写入和更新性能较差,只适合全文搜索,不适合更新频繁的交易类数据。

4.3 ES 机制

Elasticsearch 是面向文档型数据库,一条数据就是一个文档,用 JSON 作为文档序列化的格式:

json
{
  "name": "John",
  "sex": "Male",
  "age": 25,
  "birthDate": "1990/05/01",
  "about": "I love to go rock climbing",
  "interests": ["sports", "music"]
}

ES 比较适合存储非结构化或半结构化数据

4.4 ES 与关系型数据库术语对照

关系数据库Elasticsearch
数据库(Database)索引(Index)
表(Table)类型(Type)
行(Row)文档(Document)
列(Column)字段(Field)