1. Milvus 索引类型详细介绍
1.1 FLAT 索引
- 特点:FLAT 是最简单直接的索引方式,它直接存储原始向量数据,不做任何压缩或转换。在搜索时,会对集合中的所有向量进行遍历,计算其与查询向量的相似度。
- 优点
- 搜索结果精确,能保证 100% 的召回率,因为会遍历所有向量。
- 无需额外的索引构建过程,插入数据后可立即进行搜索,使用简单。
- 缺点
- 搜索效率极低,随着数据量的增加,搜索时间会呈线性增长,性能较差。
- 适用场景
- 数据量较小的场景,对搜索效率要求不高,但对结果准确性要求极高。
- 用于验证其他索引类型的搜索结果准确性。
search
参数示例
search_params = {
"metric_type": "L2", # 距离度量类型,这里使用欧氏距离
"params": {}
}
1.2 IVF(Inverse File Index)索引
- 特点:将整个向量空间划分为多个簇(倒排表),每个簇有一个质心向量。搜索时,先找出与查询向量最接近的几个簇,然后只在这些簇内进行向量搜索。
- 优点
- 相比 FLAT 索引,搜索效率显著提高,尤其是在大规模数据场景下,通过缩小搜索范围减少计算量。
- 可以通过调整簇的数量(
nlist
)和搜索时访问的簇数量(nprobe
)来平衡搜索精度和速度。
- 缺点
- 搜索结果是近似的,存在一定的召回率损失。
- 需要额外的时间和空间来构建索引,计算簇的质心向量。
- 适用场景
- 大规模数据的向量搜索场景,对搜索效率有一定要求,能接受一定的召回率损失。
- 适用于大多数实际应用,如图像检索、推荐系统等。
search
参数示例
search_params = {
"metric_type": "L2",
"params": {
"nprobe": 10 # 搜索时访问的簇数量,值越大越精确但速度越慢
}
}
1.3 HNSW(Hierarchical Navigable Small World)索引
- 特点:基于图结构的索引,构建分层的小世界图。搜索时从图的高层开始,逐步向下层搜索,直到找到最接近的向量。
- 优点
- 在高维向量空间中搜索效率高,性能优于 IVF 索引。
- 搜索结果精度较高,召回率损失相对较小。
- 支持动态数据插入和删除,无需重新构建整个索引。
- 缺点
- 索引构建时间长,需要较多内存来存储图结构。
- 随着数据量增加,索引维护成本升高。
- 适用场景
- 高维向量数据的搜索场景,如自然语言处理中的文本嵌入向量搜索。
- 对搜索精度和效率都有较高要求,且数据动态变化频繁的场景。
search
参数示例
search_params = {
"metric_type": "L2",
"params": {
"ef": 20 # 搜索时的动态访问列表大小,值越大越精确但速度越慢
}
}
1.4 ANNOY(Approximate Nearest Neighbors Oh Yeah)索引
- 特点:基于二叉树的近似最近邻搜索算法,构建多个二叉树,搜索时在多个二叉树中搜索并合并结果。
- 优点
- 索引构建速度快,占用内存相对较小。
- 在低维向量空间中搜索效率较高。
- 缺点
- 搜索结果精度较低,召回率损失较大。
- 不支持动态数据插入和删除,数据更新时需重新构建索引。
- 适用场景
- 低维向量数据的搜索场景,如地理位置数据搜索。
- 对搜索效率要求高,但对精度要求相对较低的场景。
search
参数示例
search_params = {
"metric_type": "L2",
"params": {
"search_k": 100 # 搜索时访问的节点数量,值越大越精确但速度越慢
}
}
2. 构建索引的步骤
以下是使用 PyMilvus 构建不同索引类型的示例代码:
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
# 连接到 Milvus
connections.connect(alias="default", host='localhost', port='19530')
# 定义集合字段
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=False),
FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=128)
]
# 创建集合模式
schema = CollectionSchema(fields=fields, description="Example collection")
# 创建集合
collection = Collection(name="my_collection", schema=schema)
# 插入一些示例数据
import random
ids = [1, 2, 3]
vectors = [[random.random() for _ in range(128)] for _ in range(3)]
data = [ids, vectors]
collection.insert(data)
collection.flush()
# 构建 IVF 索引
ivf_index_params = {
"metric_type": "L2",
"index_type": "IVF_FLAT",
"params": {"nlist": 128}
}
collection.create_index(field_name="vector", index_params=ivf_index_params)
# 构建 HNSW 索引
hnsw_index_params = {
"metric_type": "L2",
"index_type": "HNSW",
"params": {"M": 16, "efConstruction": 50}
}
collection.create_index(field_name="vector", index_params=hnsw_index_params)
# 加载索引到内存
collection.load()
# 搜索示例
query_vector = [[random.random() for _ in range(128)]]
# 以 IVF 索引搜索为例
results = collection.search(
data=query_vector,
anns_field="vector",
param=search_params,
limit=10
)
# 断开连接
connections.disconnect("default")
3. 有索引跟没索引的区别
3.1 搜索效率
- 无索引:进行全量向量遍历搜索,对集合中每个向量计算相似度。数据量增大时,搜索时间显著增长,效率极低。
- 有索引:通过索引结构缩小搜索范围,避免全量遍历,提高搜索效率。如 IVF 索引只在部分簇内搜索,HNSW 索引利用图结构快速定位。
3.2 搜索精度
- 无索引:全量遍历保证搜索结果精确,召回率 100%。
- 有索引:多数为近似索引,存在一定召回率损失,但可通过调整参数平衡精度和效率。
3.3 数据更新和维护成本
- 无索引:数据插入和删除简单,无需考虑索引更新维护,但数据量增加会使搜索性能下降。
- 有索引:数据更新时需考虑索引更新维护,可能增加时间和空间开销。如 HNSW 索引支持动态更新但维护成本随数据量增加而升高,ANNOY 索引不支持动态更新,更新时需重建索引。