sql两行数据合并成一行_Milvus元数据管理

我们在《Milvus在大规模向量检索场景下的数据管理》一文中介绍过一些关于元数据(meta data)的信息。Milvus使用SQLite或者MySQL来管理数据文件的状态和信息。元数据有两张表,一张叫Tables,用于记录向量表的信息;一张叫TableFiles,用于记录向量数据文件以及索引文件的状态和信息。这篇文章将会详细解释这两张meta表的每个字段,包括它们各自的含义以及Milvus在运行过程中如何使用这些字段。Milvus从今年年初开始做原型到现在已近一年,从最初的内部测试,到0.3.0版本开始有用户试用,再到现在的0.6.0版本,已经历了若干次迭代。真正开始有比较多的人注意到这个产品是从0.5.0版本开始的,实际上0.4.0和0.5.0的meta格式一模一样,因此0.4.0之前的版本我就忽略不说。

(一)怎样查看Milvus的元数据

(1)SQLite

如果使用的是SQLite,那么在Milvus启动之后就会在数据目录(在配置文件server_config.yaml的primary_path里定义)下生成一个meta.sqlite文件,我们安装一个SQLite的客户端来打开这个文件查看里面的内容。

先在命令行安装sqlite3:

sudo apt-get install sqlite3

然后命令行进入Milvus的数据目录,用sqlite3打开那个文件:

sqlite3 meta.sqlite

之后就进入了sqlite的客户端命令行,我们再使用这几行命令来看看元数据里到底有啥。前两行是让打印结果排版易于人类阅读,后面两行是SQL语句,用于查询Tables和TableFiles两张表的内容(大小写无所谓)。

.mode column
.header on
SELECT * FROM Tables;
SELECT * FROM TableFiles;

4f37611ec9ede4a27034df280cd43e77.png

(2)MySQL

如果使用的是MySQL,需要在配置文件server_config.yaml的backend_url指明MySQL服务的地址。比如下面这个设置表示MySQL服务部署在本地,端口3306,用户名root,密码123456,数据库名称是milvus:

db_config:
   backend_url: mysql://root:123456@127.0.0.1:3306/milvus

安装MySQL的客户端:

sudo apt-get install default-mysql-client

当你启动了Milvus之后,Milvus就会在backend_url指定的MySQL服务里创建Tables和TableFiles两张表,然后我们用命令行连接MySQL服务:

mysql -h127.0.0.1 -uroot -p123456 -Dmilvus

这样,我们可以用SQL语句来查询元数据信息了:

9ae6fa9c2d5c1c2fc7f028f81d6e5b00.png

Tables表的schema

我们以SQLite为例。下图显示的打印结果是0.5.0版本的示例。0.6.0版本比0.5.0多了几个字段,后面再介绍。这个Tables里有一行记录,表示有一张向量表,它的表名叫table_1,是512维度的,建表时设置的index_file_size是1024MB,索引类型(engine_type)是1(FLAT),nlist是16384,metric_type是1(欧氏距离L2)。
另外还有字段id是该表的内部唯一标识,state是表的状态0(正常),created_on是创建的时间,flag是给内部使用预留的标志位信息。

a62f1abb9d3d2a1e94af9c4f26f0a3de.png

Tables的各个字段类型及其简介如下表所示:

cb579ad868575e616cadbbee23a2804b.png

在0.6.0版本里我们增加了分区功能,因此多了几个字段,如下图所示。这里我们看到多了owner_table,partition_tag和version几个字段。这个示例里面有一张向量表,表名叫table_1,它有一个分区叫table_1_p1。可以看到分区在内部实际上是以一张向量表的形式存在的,partition_name实际上对应于table_id,分区表的参数都继承自它的母表,owner_table字段记录了母表的名字,partition_tag则是分区表的标签(tag)。

d5265a990d72b7cdf48991897d61be6b.png

0.6.0新增的几个字段类型及简介如下表所示:

aea43ca5eb7063a8ce35ac2c193fe551.png

TableFiles表的schema

下面这个例子里有两个文件,它们都属于table_1向量表。第一个文件的索引类型(engine_type)是1(FLAT),文件状态(file_type)是7(备份文件),文件大小是411200113字节,向量行数是20万条。第二个文件除了索引类型是2(IVFLAT),文件状态为3(索引文件),它实际上是第一个文件的索引,后面我们会介绍。

8bc7f94dace031f9ff58bf0581f5b8e8.png

TableFiles的各个字段类型及其简介如下表所示:

a296ceb6c3eecca2beea703d8f1550eb.png

(二)Milvus如何通过元数据管理数据文件

通过上面的介绍我们了解了Milvus的元数据里有些什么信息,现在我们来看这些信息是怎样被使用的。我们仍以SQLite为例。

(1)创建向量表

我们用python客户端创建一张表:

milvus.create_table({
    'table_name': 'table_1',
    'dimension': 512,
    'index_file_size': 1000,
    'metric_type': MetricType.L2
})

Milvus立即会在Tables里增加一行记录,dimension为512,index_file_size为1048576000字节(1000乘1024再乘1024),metric_type为1(欧氏距离L2)。而TableFiles里仍然是空的。

增加一行记录是用SQL语句完成的:

INSERT INTO Tables VALUES(1, 'table_1', 0, 512, 1576306272821064, 2, 1048576000, 1, 16384, 1, , , '0.6.0');

用SQLite客户端去查看Tables的信息,这时我们看到这张表的engine_type和nlist都是默认值:

7428e65148451d4de3e484fc811a8afd.png

(2)插入向量

之后我们插入一些向量到这张表里:

milvus.insert(table_name='table_1', records=vec_list, ids=vec_ids)

假设我们分批每次1万条向量插入,总共插入了100万条512维的向量,如果在插入过程中去查看TableFiles里的信息的话,你会看到TableFiles里面不断有新条目生成并且不断地删除一部分旧条目,这是因为合并文件的线程在不断地把小文件合并成大文件,并删除小文件。

当100万条向量插入完成后你再去查询TableFiles,大致会看到最终有两个文件留下:

6ea89dd13fc3831b9f228f4dd287864d.png

从row_count字段可以看到,第一个文件有53万条向量,另一个有47万条向量,这是因为在合并文件过程中第一个文件被合并到超过1048576000字节后就不再参与合并(我们可以看到它的大小是1089680113字节)。剩下的向量则被合并到第二个文件里,最终达到966320113字节,还没达到index_file_size的大小,这意味着如果还有向量进来的话,这个文件仍会被拿来和其他小文件做合并。

Milvus内部对TableFiles的操作也都是通过SQL完成,主要借助两种语句:

INSERT INTO TableFiles VALUES(...);
DELETE FROM TableFiles WHERE ...;

在Milvus的数据目录里面你也能找到这两个文件,我这里设的数据目录是/tmp/milvus,每个向量表都有独立的目录,这两个文件就在/tmp/milvus/db/tables/table_1下面:

836f629d5581b0fde80d1d51f4d4718e.png

(3)查询向量条数

客户端通过count_table来获得这个表有多少条向量:

milvus.count_table(table_name='table_1')

Milvus内部会执行一条SQL查询:

SELECT SUM(row_count) FROM TableFiles where table_id = 'table_1' AND file_type IN (1, 2, 3);

学过SQL的应该很容易看出来这条语句的意思,它是把TableFiles里所有符合条件记录的row_count字段的值相加,得出这个表总共有多少条向量。要符合什么条件呢?首先表名要是”table_1“;其次只统计文件状态为1,2,3的条目,也就是说,只统计原始向量文件,将要建立索引的文件,建立好索引的文件。如果有文件状态为4(软删除状态)或者7(备份状态)的,是不参与统计的。

(4)搜索向量

客户端通过search来搜索向量:

milvus.search(table_name='table_1', query_records=query_vectors, top_k=100, nprobe=32)

Mlvus内部执行一条SQL语句来获得需要被检索的文件:

SELECT * FROM TableFiles WHERE table_id = 'table_1' AND file_type IN (1, 2, 3);

这样就获得了所有需要被检索的文件信息,同样,只有文件状态为1,2,3的文件会被拿来检索。接着Milvus会通过文件的file_id找到它们所在的路径,之后查询调度器会把这些文件逐个加载进内存或者显存计算。

(5)建立索引

客户端通过create_index来建立索引,下面这个调用是建立一个SQ8索引,我们指定nlist为5000:

milvus.create_index(table_name='table_1', {'index_type': IndexType.IVF_SQ8, 'nlist': 5000})

如果我们这时去查看这张表的信息,就会看到有所变化:

ef0b7532c7d0c83a00e83e2eecd990b2.png

该表的目标索引类型以及nlist都相应做了改变,这是Milvus在内部执行了SQL的UPDATE操作:

UPDATE Tables SET engine_type = 3, nlist = 5000 WHERE table_id = 'table_1';

接着,Milvus把属于该表的能够检索引的文件状态置为2(将要被建立索引),也是通过SQL操作:

UPDATE TableFiles SET file_type = 2 WHERE table_id = 'table_1' AND file_type = 1;

这时,客户端的create_index调用仍然在等待,一直等到全部文件建立索引完成。Milvus里会不断地检查是否有新的原始向量文件生成,如果有,则立刻把它们的file_type置为2(将要建立索引)。而调度器会为file_type为2的文件建立任务,逐个建立索引。直到所有文件都建立了索引,客户端调用才会真正返回。

当索引建立完成后,会有新的索引文件生成,而之前的原始向量文件则会被标记为备份状态(file_type置为7),这是为了之后能够切换成别的索引类型。

6d5b33e4c1bb043e3a7463d633480cc2.png

上图我们看到多了两个文件,它们的row_count对应于之前的两个原始向量文件,而它们的file_size比之前两个文件都小很多,这是因为SQ8这种索引类型对数据做了简化,所需要的存储空间变少了。从engine_type和file_type我们可以看出这两组文件的区别。

(6)删除索引

客户端通过drop_index来删除索引:

milvus.drop_index(table_name='table_1')

在Milvus内部,删除索引操作要做几件事,先是把向量表的索引类型切换回1(FLAT),然后把索引文件的file_type置为4(软删除),同时把备份文件的file_type切换为1(原始向量文件):

UPDATE Tables SET engine_type = 1 WHERE table_id = 'table_1';
UPDATE TableFiles SET file_type = 4 WHERE table_id = 'table_1' AND file_type = 3;
UPDATE TableFiles SET file_type = 1 WHERE table_id = 'table_1' AND file_type = 7;

负责清理数据的线程会拿到需要被删除的文件信息,然后找到文件位置将其从磁盘上真正删除,接着索引文件的条目也会从TableFiles中移除。

DELETE FROM TableFiles WHERE table_id = 'table_1' AND file_type = 4;

(7)删除向量表

客户端通过drop_table来删除向量表:

milvus.drop_table(table_name='table_1')

Milvus内部会把向量表的state置为1(软删除),然后把该表的所有文件的file_type置为4(软删除):

UPDATE Tables SET state = 1 WHERE table_id = 'table_1';
UPDATE TableFiles SET file_type = 4 WHERE table_id = 'table_1' ;

负责清理数据的线程会拿到需要被删除的文件信息,然后找到文件位置将其从磁盘上真正删除,接着这些文件的条目也会从TableFiles中移除,向量表条目从tables中移除。

DELETE FROM TableFiles WHERE table_id = 'table_1' AND file_type = 4;
DELETE FROM Tables WHERE state = 1;

(三)总结

通过以上介绍,应该不难看出Milvus使用元数据的套路:修改某些条目的状态,根据条目信息做相应的操作(索引,删除)。具体的实现上有一些技巧,比如需要借助OLTP数据库的事务机制来避免某些问题,中途出错时需要把操作回退等等。Milvus内部定义了元数据管理的接口,其实不光是SQL数据库,我们甚至可以用NoSQL数据库来管理Milvus的元数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值