参考https://milvus.io/cn/docs/home。Milvus 是一款基于云原生架构开发的开源向量数据库,支持查询和管理由机器学习模型或神经网络生成的向量数据。Milvus 在一流的近似最近邻(ANN)搜索库(例如 Faiss、NMSLIB、Annoy)的功能基础上进行扩展,具有按需扩展、流批一体和高可用等特点。
下面介绍几种安装方式:
# Clone github repository.
$ git clone https://github.com/milvus-io/milvus.git
# Install third-party dependencies.
$ cd milvus/
$ ./scripts/install_deps.sh
# Compile Milvus.
$ make
这里是一些注意事项:https://zhuanlan.zhihu.com/p/91444753
另外可以参考一下训练营,benchmark测试也在里面。给出的案例包括:
Milvus 数据段存储海量数据。在建立索引时,Milvus 为每个数据段单独创建索引。
调用 create_index() 接口时,Milvus 会对该字段上的已有数据同步建立索引。每当后续插入的数据的大小达到系统配置的 index_file_size 时,Milvus 会为其在后台自动创建索引。
当插入的数据段少于 4096 行时,Milvus 不会为其建立索引。
众所周知,建索引是一个比较消耗计算资源和时间的工作。当查询任务和后台建索引任务并发时,Milvus 通常把计算资源优先分配给查询任务,即用户发起的任何查询命令都会打断后台正在执行的建索引任务。之后仅当用户持续 5 秒不再发起查询任务,Milvus 才会恢复执行后台建索引任务。此外,如果查询命令指定的数据段尚未建成指定索引,Milvus 会直接在段内做全量搜索。
建立集合时,Milvus 根据参数 index_file_size 控制数据段的大小。另外,Milvus 提供分区功能,你可以根据需要将数据划分为多个分区。对数据的合理组织和划分可以有效提高查询性能。
数据段(segment)
为了能处理海量的数据,Milvus 会将数据分段,每段数据拥有数万甚至数十万个实体。每个数据段的数据又按照字段(field)分开,每个字段的数据单独存为一个数据文件。目前的版本中,实体仅包含一个 ID 字段和一个向量字段,因此每个数据段的数据文件主要包括一个 UID 文件以及一个原始向量数据文件。
数据段的大小是由创建集合时的参数 index_file_size 来决定的,默认为 1024 MB,上限为 128 GB。
建立索引时,集合中的每个数据段依次建立索引,并将索引单独存为一个文件。索引文件之间相互独立。索引可以显著地提高检索性能。
分区(partition)
当一个集合累积了大量数据之后,查询性能会逐渐下降。而某些场景只需查询集合中的部分数据,这时就要考虑把集合中的数据根据一定规则在物理存储上分成多个部分。这种对集合数据的划分就叫分区。每个分区可包含多个数据段。
分区以标签(tag)作为标识。插入向量数据时,你可以指定将数据插入到某个标签对应的分区中。查询向量数据时,你可以根据标签来指定在某个分区的数据中进行查询。Milvus 既支持对分区标签的精确匹配,也支持正则表达式匹配。
每个集合的分区数量上限是 4096 个
客户端通过调用 insert 接口来插入数据,单次插入的数据量不能大于 256 MB。插入数据的流程如下:
服务端接收到插入请求后,将数据写入预写日志(WAL)。
当预写日志成功记录后,返回插入操作。
将数据写入可写缓冲区(mutable buffer)。
每个集合都有独立的可写缓冲区。每个可写缓冲区的容量上限是 128 MB。所有集合的可写缓冲区总容量上限由系统参数 insert_buffer_size 决定,默认是 1 GB。
缓冲区中的数据落盘有三种触发机制:
定时触发
系统会定时触发落盘任务。定时间隔由系统参数 auto_flush_interval 决定,默认是 1 秒。
落盘操作的流程如下:
系统开辟一块新的可写缓冲区,用于容纳后续插入的数据。
系统将之前的可写缓冲区设为只读(immutable buffer)。
系统把只读缓冲区的数据写入磁盘,并将新数据段的描述信息写入元数据后端服务。
完成以上流程后,系统就成功创建了一个数据段(segment)。
客户端触发
由客户端调用 flush 接口触发落盘。
缓冲区达到上限触发
累积数据达到可写缓冲区的上限(128MB)会触发落盘操作。
每个数据段的所有相关文件都被存放在以段 ID 命名的文件夹中,比如记录实体 ID 的 UID 文件、用于标记已被删除实体的 delete_docs 文件,以及用于快速查找实体的布隆过滤器(bloom-filter)文件。
小数据段过多会导致查询性能低下。为了避免此问题,Milvus 会在需要的时候触发后台段合并任务,即把小数据段合并成新的数据段,并删除小数据段、更新元数据。其中,新数据段的大小不低于 index_file_size。
合并操作的触发时机如下:
启动服务时
完成落盘任务后
建索引前
删除索引后
删除集合:客户端调用 drop_collection 接口来删除一个集合。
服务端接收到请求后,仅在元数据中把该集合(包括它的分区和段)标记为删除状态。对于已标记为删除状态的集合,将无法再对其进行任何新操作(比如插入和查询)。
后台的清理任务将被标记为删除状态的集合(包括它的分区和段)从元数据中删除,然后将该集合的数据文件和文件夹从磁盘上删除。如果在删除操作之前已经有对该集合的操作正在执行,后台清理任务不会删除正在使用的段,直到操作完成。
删除分区:客户端调用 drop_partition 接口来删除一个分区。
服务端接收到请求后,仅在元数据中把该分区(包括它的段)标记为删除状态。
后台清理任务按照删除集合的流程来删除该分区和元数据。
删除实体:Milvus 为每个数据段建立了一个 delete_docs 文件,用来记录被删除向量在段内的位置。
Milvus 使用布隆过滤器(bloom filter)来快速判断一个实体 ID 是否可能存在于某个数据段中。因此,在每个数据段下都创建了一个名为 bloom_filter 的文件。
删除实体的流程如下:
客户端调用 delete_entity_by_id 接口删除集合中的实体。
服务端接收到请求后,执行以下操作删除实体:
如果该实体在插入缓冲区中,直接删除该实体。
否则,根据每个数据段的布隆过滤器判断该实体所处的数据段,然后更新该数据段的 delete_docs 以及 bloom_filter 文件。
数据段整理:查询一个数据段时,Milvus 会将该数据段的实体数据以及 delete_docs 文件读入内存。虽然被删除的实体不参与计算,但它们也会被读入内存。所以,一个数据段中被删除的实体越多,浪费的内存资源和磁盘空间越多。为了减少此类不必要的资源消耗,Milvus 提供了数据段整理(compact)的操作,流程如下:
客户端调用 compact 接口。
服务端接收到请求后,根据 delete_docs 所记录的信息,将段内未被删除的实体写入一个新的数据段,并把旧数据段标记为删除状态。之后将由后台清理任务负责清理被标记为删除状态的数据段。如果旧数据段已建立索引,新数据段产生之后会重建索引。
compact 操作会忽略被删除向量占比小于 10% 的数据段。
从SIFT1B Dataset(10亿)中提取数据进行性能测试。测试过程见https://github.com/milvus-io/bootcamp/blob/master/benchmark_test/lab1_sift1b_1m.md和https://github.com/milvus-io/bootcamp/blob/master/benchmark_test/lab2_sift1b_100m.md
使用以下任意一种方法连接 Milvus 服务端:
milvus = Milvus(host='localhost', port='19530')
milvus = Milvus(uri='tcp://localhost:19530')
milvus.drop_collection(collection_name='test01')
创建集合:相当于建表
param = {'collection_name':'test01', 'dimension':256, 'index_file_size':1024, 'metric_type':MetricType.L2}
milvus.create_collection(param)
创建分区:你可以通过标签将集合分割为若干个分区,从而提高搜索效率。每个分区实际上也是一个集合。
milvus.create_partition('test01', 'tag01')
milvus.drop_partition(collection_name='test01', partition_tag='tag01')
插入向量时,如果你不指定向量 ID,Milvus 自动为向量分配 ID。Milvus 中数据是分文件存储的,后续新增向量会存在新的数据文件中。该文件达到一定量后会自动触发建立索引,生成一个新的索引文件,不会影响之前已经建立过的索引。
import random
# Generate 20 vectors of 256 dimensions.
vectors = [[random.random() for _ in range(256)] for _ in range(20)]
milvus.insert(collection_name='test01', records=vectors)
vector_ids = [id for id in range(20)]
milvus.insert(collection_name='test01', records=vectors, ids=vector_ids)
milvus.insert('test01', vectors, partition_tag="tag01")
假设你的集合中存在以下向量 ID:ids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],你可以通过以下命令删除向量:
milvus.delete_entity_by_id(collection_name='test01', id_array=ids)
create_index() 会指定该集合的索引类型,并同步为之前插入的数据建立索引,后续插入的数据在大小达到 index_file_size 时,索引会在后台自动建立。在实际生产环境中,如果是流式数据,建议在插入向量之前先创建索引,以便后续系统自动建立;如果是静态数据,建议导入所有数据后再一次性创建索引。更多索引用法请参考https://github.com/milvus-io/pymilvus/blob/master/examples/old_style_example_index.py。
ivf_param = {'nlist': 16384}
# Create an index.
milvus.create_index('test01', IndexType.IVF_FLAT, ivf_param)
milvus.drop_index('test01')
删除索引后,集合再次使用默认索引类型 FLAT。
对于不同的索引类型,搜索所需参数也有区别。所有的搜索参数都必须赋值。详细信息请参考 Milvus 索引类型。
search_param = {'nprobe': 16}
# Create 5 vectors of 256 dimensions.
q_records = [[random.random() for _ in range(256)] for _ in range(5)]
milvus.search(collection_name='test01', query_records=q_records, top_k=2, params=search_param)
如果你不指定 partition_tags, Milvus 会在整个集合中搜索。下面是在分区中查询向量
# Create 5 vectors of 256 dimensions.
q_records = [[random.random() for _ in range(256)] for _ in range(5)]
milvus.search(collection_name='test01', query_records=q_records, top_k=1, partition_tags=['tag01'], params=search_param)
milvus.flush(collection_name_array=['test01'])
milvus.compact(collection_name='test01', timeout=1)
# -*- coding: utf-8 -*-
# 导入相应的包
import numpy as np
from milvus import Milvus, MetricType
# 初始化一个Milvus类,以后所有的操作都是通过milvus来的
milvus = Milvus(host='localhost', port='19530')
# 向量个数
num_vec = 5000
# 向量维度
vec_dim = 768
# name
collection_name = "test_collection"
# 创建collection,可理解为mongo的collection
collection_param = {
'collection_name': collection_name,
'dimension': vec_dim,
'index_file_size': 32,
'metric_type': MetricType.IP # 使用内积作为度量值
}
milvus.create_collection(collection_param)
# 随机生成一批向量数据
# 支持ndarray,也支持list
vectors_array = np.random.rand(num_vec, vec_dim)
# 把向量添加到刚才建立的collection中
status, ids = milvus.insert(collection_name=collection_name, records=vectors_array) # 返回 状态和这一组向量的ID
milvus.flush([collection_name])
# 输出统计信息
print(milvus.get_collection_stats(collection_name))
# 创建查询向量
query_vec_array = np.random.rand(1, vec_dim)
# 进行查询,
status, results = milvus.search(collection_name=collection_name, query_records=query_vec_array, top_k=5)
print(status)
print(results)
# 如果不用可以删掉
status = milvus.drop_collection(collection_name)
# 断开、关闭连接
milvus.close()
如下代码安装控制台
docker pull milvusdb/milvus-em:v0.4.2
docker run -d -p 3000:80 milvusdb/milvus-em:v0.4.2
登录http://localhost:3000/,在url填入http://localhost:19121,就可以进行管理界面了