milvus数据库初探

1.安装(单机版)

1.1.安装 docker 及 docker-compose

官方建议,docker 安装 19.03 或以上版本,docker-compose 安装 1.25.1 或以上版本。

官方安装前提:https://milvus.io/cn/docs/v2.0.0/prerequisite-docker.md

实际测试时,docker 18.09 docker-compose 1.24.1 也可正常安装。但是还是建议安装官方推荐的版本。

docker 及 docker-compose 的安装教程,可参考:https://www.jianshu.com/p/f760fa6a0b03

1.2.下载配置文件

wget https://github.com/milvus-io/milvus/releases/download/v2.0.0-rc8/milvus-standalone-docker-compose.yml

1.3. 修改配置文件名称

mv milvus-standalone-docker-compose.yml docker-compose.yml

1.4.拉取镜像并启动

sudo docker-compose up -d
milvus安装成功截图

镜像启动后,有这三个容器就表示启动成功了。

Milvus:负责提供系统的核心功能。

Etcd: 是元数据引擎,用于管理 Milvus 内部组件的元数据访问和存储,例如 proxy、index node 等。

MinIO: 是存储引擎,负责维护 Milvus 的数据持久化。

1.5.可视化组件安装(可选)

可视化组件也是通过docker安装的,安装完成后,在浏览器输入:http://{your_ip}:3000 即可看到milvus的操作管理页面,http://{your_ip}:3000/api 可查看milvus的Swagger API管理页面。

docker run -p 3000:3000 -e HOST_URL=http://{your_ip}:3000 -e MILVUS_URL={your_ip}:19530 milvusdb/milvus-insight:latest

2.milvus 术语

https://milvus.io/cn/docs/v2.0.0/glossary.md
Collection:包含一组 entity,可以等价于关系型数据库系统(RDBMS)中的表。

Entity:包含一组 field。field 与实际对象相对应。field 可以是代表对象属性的结构化数据,也可以是代表对象特征的向量。primary key 是用于指代一个 entity 的唯一值。

你可以自定义 primary key,否则 Milvus 将会自动生成 primary key。请注意,目前 Milvus 不支持 primary key 去重,因此有可能在一个 collection 内出现 primary key 相同的 entity。

Field: Entity 的组成部分。Field 可以是结构化数据,例如数字和字符串,也可以是向量。

Milvus 2.0 现已支持标量字段过滤。

Segment: Milvus 在数据插入时通过合并数据自动创建的数据文件。一个 collection 可以包含多个 segment。一个 segment 可以包含多个 entity。在搜索中,Milvus 会搜索每个 segment,并返回合并后的结果。

Sharding: Shard 是指将数据写入操作分散到不同节点上,使 Milvus 能充分利用集群的并行计算能力进行写入。默认情况下单个 collection 包含 2 个分片(shard)。目前 Milvus 采用基于主键哈希的分片方式,未来将支持随机分片、自定义分片等更加灵活的分片方式。

Partition 的意义在于通过划定分区减少数据读取,而 shard 的意义在于多台机器上并行写入操作。

Partition: 把 collection 中的数据根据一定规则在物理存储上分成多个部分。这种对 collection 数据的划分就叫分区(partitioning)。每个 partition 可包含多个 segment。

单机部署: 一种 Milvus 的部署方式。在单机部署模式下,数据插入、索引构建、近似搜索等所有操作都在一个进程中完成。

归一化: 归一化指的是通过数学变换将向量的模长变为 1 的过程。如需使用点积计算向量相似度,则必须对向量作归一化处理。处理后点积与余弦相似度等价。

集群部署: 一种 Milvus 的部署方式。在集群部署模式下,Milvus 服务由一组节点共同提供,可实现高可用和易扩展。

索引: 索引基于原始数据构建,可以提高对 collection 数据搜索的速度。Milvus 支持多种索引类型。

向量: 一种类型的 field,代表对象的特征。非结构化数据可以通过各种 AI 模型和 embedding 技术转化为向量。

目前,一个实体最多只能包含一个向量。

3.python 操作 milvus

3.1.安装包

pip install pymilvus-orm==2.0.0rc4

安装的版本最好跟前面的 milvus 数据库版本一致。

3.2.连接 milvus

# 连接milvus
milvus = connections.connect(host='xx.xx.xx.xx', port='19530')
# 输出milvus的所有collections
print(milvus.list_collections())

3.3.创建 collection

# 主键,并设置为自增id
field_id = FieldSchema(name="field_id", dtype=DataType.INT64, is_primary=True, auto_id=True)
# 向量检索的field,只有FLOAT_VECTOR类型的field,才有维度dim参数
field = FieldSchema(name=field_name, dtype=DataType.FLOAT_VECTOR, dim=768)
# 可作为向量与文本的对应关系
text_id = FieldSchema(name="text_id", dtype=DataType.INT64)
schema = CollectionSchema(fields=[field_id, field, text_id], description="example collection create by tianyunzqs")

collection = Collection(name=collection_name, schema=schema)
print(get_connection().has_collection(collection_name))
print(get_connection().list_collections())

3.4.创建分区

个人想法:分区的目的是将 collection 的数据分成几部分存储,基于该特性,如果多个业务产生的向量数量不多,可根据不同业务产生的向量,分区存储,检索的时候,也可以指定分区检索,可以提高资源利用率。

collection = Collection(name='collection_test')
partition = collection.create_partition("partition_name")
print(collection.partitions)
print(collection.has_partition(partition_name))

3.5.创建索引

关于索引的参数解释,可参考:https://milvus.io/cn/docs/v2.0.0/index_selection.md

# 为向量检索的field 创建索引
# IP表示内积,当向量归一化后,内积与余弦相似度等价
index_param = {
    "metric_type": "IP",
    "index_type": "IVF_FLAT",
    "params": {"nlist": 1024}
}
collection.create_index(field_name=field_name, index_params=index_param)
print(collection.index().params)

在创建索引的时候,需要指定的nlist的值,该怎么确定,官方给出了一个推荐值:如何设置 IVF 索引的 nlist 和 nprobe 参数?

IVF 索引的 nlist 值应根据具体使用情况设置。一般来说,建议值为 4 × sqrt(n),其中 n 指 segment 最多包含的 entity 条数。

其中,entity 条数可通过属性collection.num_entities来得到,也就是插入collection中的数据条数。

3.6.插入数据

def insert(collection: Collection, partition_name=None):
    """
    插入数据
    :param partition_name: 指定插入的分区
    :param collection:
    :return:
    """
    # 由于主键field_id设置自增,所以无需插入
    mr = collection.insert([
        # 只能是list
        np.random.random([100, 8]).tolist(),  # 向量
        np.random.randint(0, 10, [10000]).tolist()  # cat_id
    ], partition_name=partition_name)
    print(mr.primary_keys)

    # 插入的数据存储在内存,需要传输到磁盘
    get_connection().flush([collection.name])

3.7.向量检索

def search(collection: Collection, partition_name=None):
    """
    向量检索
    :param collection:
    :param partition_name: 检索指定分区的向量
    :return:
    """
    # 将collection加载到内存,必须先加载到内存,然后才能检索
    collection.load()
    search_params = {"metric_type": "IP", "params": {"nprobe": 10}}
    # 向量搜索
    result = collection.search(data=np.random.random([2, 8]).tolist(),
                               anns_field=field_name, param=search_params, limit=2,
                               partition_names=[partition_name] if partition_name else None)
    for res in result:
        print(res.ids)  # 查询出来id后,根据id找到对应的text

在向量检索的时候,需要指定nprob参数,该参数表示
那么到底该值如何确定,官方给的答案是

nprobe 值的选取需要根据数据总量和实际场景在查询性能和准确率之间进行取舍。建议通过多次实验确定合理取值。

3.8.删除索引

collection = Collection(name='collection_test')
collection.drop_index()

3.9.删除分区

collection = Collection(name='collection_test')
collection.drop_partition("partition_name")

3.10.删除 collection

collection = Collection(name='collection_test')
collection.drop()

3.11.从内存中释放 collection

collection = Collection(name='collection_test')
if collection:
    collection.release()
# 断开与服务器的连接,释放资源,alias默认为"default"
connections.disconnect(alias="default")

4.问题汇总

https://milvus.io/cn/docs/v2.0.0/product_faq.md

Milvus 支持非 x86 平台吗?
Milvus 暂不支持非 x86 平台。

为保证 Milvus 的正常运行,你的 CPU 须支持以下任一指令集:SSE4.2、AVX、AVX2、AVX512。这些均为 x86 平台专用的 SIMD 指令集。

Milvus 可以处理百亿或千亿级数据吗?
理论上来说,Milvus 能够处理的数据规模取决于用户自身的硬件条件,其中有两大关键指标,即系统内存容量和持久化存储空间容量。

执行查询操作前,Milvus 需先将所有指定的 collection 或 partition 加载到内存。因此,内存容量决定了 Milvus 可查询数据的上限。

执行插入操作时,Milvus 需先将所有的 entity 以及 collection 相关的 schema (当前仅支持 MinIO 作为持久化存储)全部写入持久化存储。因此,持久化存储空间的容量决定了 Milvus 可插入数据的上限。

Milvus 的 Python SDK 有连接池吗?
Milvus v0.9.0 及更高版本对应的 Python SDK 有连接池。连接池的连接数量没有上限。

Milvus 是否支持 “边插入边查询” ?
支持。插入操作和查询操作由两个相互独立的模块分开执行,因此互不影响。对于客户端,插入数据进入消息队列即意味着该插入操作结束,尽管此时的数据可能还无法被查询到。只有加载到 query node 的数据才能被用户查询到。若插入的 segment 的大小未满足构建索引的阈值(默认值为 512 MB),Milvus 将使用暴搜,这种情况下的查询性能会受到一定影响。

Milvus 允许插入重复 ID 的向量吗?
允许,Milvus 不会对向量 ID(即 primary key)进行去重。

如果插入重复 ID(即 primary key)的向量,Milvus 是否会将其作为数据更新处理?
目前,Milvus 的去重功能无法保证插入新数据会覆盖与其 pk 相同的旧数据。因此,当前版本中基于相同 pk 的结构化匹配的返回结果为未知行为。该限制将在未来版本中修复。

Milvus 中单次插入数据有上限吗?
因 gRPC 限制,单次插入数据不能超过 1024 MB。

搜索指定 partition 时,如果所在的 collection 大小发生变化,是否对查询性能有影响?
不会。如果你在搜索时指定了 partition,Milvus 只会在相应 partition 进行搜索。

如果已指定仅搜索部分 partition,Milvus 会将整个 collection 的数据加载到内存吗?
不会。查询前需先保证数据已加载到内存。

如果明确知道当前数据所在 partition,可直接调用 load_partition() 方法加载指定 partition 的数据,然后调用 search() 方法并指定该 partition。

如果不确定数据所在 partition,那么在调用 search() 方法前需先调用 load_collection() 方法。

如果未在查询前加载 collection 或 partition 数据,Milvus 会报错。

Milvus 支持新增向量后再建索引吗?
支持。调用 create_index() 方法后,Milvus 会为后续新增向量自动构建索引的任务。每当新增数据量达到一个完整的 segment 时即触发这一任务,Milvus 为新插入的向量构建索引。

新增向量的索引文件与前期构建的索引文件相互独立。

Milvus 中 FLAT 索引和 IVF_FLAT 索引的原理比较?
IVF_FLAT 索引将向量空间分成 nlist 个聚类单元。假设以默认值 nlist = 16,384 搜索,Milvus 会先比较这 16384 个单元的中心与目标向量之间的距离,得出最近的 nprobe 个单元,接着比较这些单元内所有向量距离,得到最接近的向量。

FLAT 则计算每条向量和目标向量之间的距离。

当向量总条数约等于 nlist 时,两者的计算量相当,无明显性能差距。然而,随着向量条数达到 nlist 的 2 倍、3 倍、n 倍之后,IVF_FLAT 的性能优势就越来越突出。

Milvus 支持的向量维度的最大值是多少?
Milvus 最多支持 32768 维向量。

Milvus 是否支持 Apple M1 CPU?
当前版本 Milvus 暂不支持 Apple M1 CPU。

Milvus 支持何种 ID (即 primary key)field 数据类型?
在当前版本中,Milvus 仅支持 INT64 数据型。未来的 2.0 正式版将会同时支持 INT64 和 string 数据型。

Milvus 支持扩缩容吗?
支持。你可以通过 Helm Chart 在 Kubernetes 上部署多节点 Milvus 集群。

查询是否在内存中执行? 什么是增量数据和历史数据?
是的。当收到查询请求时,Milvus 会将增量数据和历史数据共同加载至内存后进行搜索。增量数据来自尚未达到持久化阈值而缓存在内存中的 growing segment,而历史数据则来自已经持久化在对象存储中的 sealed segment。增量数据和历史数据共同构成了要搜索的整个数据集。

Milvus 2.0 支持并行搜索吗?
支持。 对于同一个集合上的查询,Milvus 会并行查询增量数据和历史数据,而对不同集合上的查询则是串行的。但由于历史数据可能是一个非常庞大的数据集,对历史数据的搜索相对更耗时,所以本质上是串行的。 Milvus 2.0 的正式版将改进这个问题。

相似性搜索与结构性匹配有何区别?
在 Milvus 中,向量相似度搜索是通过计算向量相似度以及向量索引的加速来检索向量。与搜索不同,向量结构性匹配是通过标量过滤来匹配检索向量。布尔表达式通过过滤标量 field 或 primary key field,检索并返回所有与之匹配的结果。 结构性匹配不涉及相似性计算以及向量索引。


https://milvus.io/cn/docs/v2.0.0/operational_faq.md

召回率主要受哪些因素影响?
召回率主要受索引类型和查询参数影响。

对于 FLAT 索引,Milvus 会在 collection 内做全量搜索,召回率为 100%。

对于 IVF 索引,nprobe 参数决定了搜索范围——nprobe 越大,搜索的数据比例越高,召回率也就越高,但查询性能会相应降低。

对于 HNSW 索引,ef 参数决定了导航图搜索的广度——ef 越大,图上扫描到的结点越多,召回率也就越高,但查询性能会相应降低。

为什么配置文件更新后没有生效?
Milvus v2.0 暂不支持运行时动态修改配置文件。配置文件更新后,必须重启 Milvus Docker 让修改生效。


https://milvus.io/cn/docs/v2.0.0/performance_faq.md

边插入边搜索会影响搜索性能吗?
数据插入本身并不是一个 CPU 密集型操作,但是由于新插入的数据所在 segment 的大小可能还未达到自动创建索引的阈值,在查询时只能对其采用暴搜的方式,查询性能就会降低。

参数 rootcoord.minSegmentSizeToEnableIndex 决定了 segment 自动构建索引的阈值,默认值为 1024。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容