存储操作

阅读本文前,请先阅读 存储相关概念

数据插入

客户端通过调用 insert 接口来插入数据,单次插入的数据量不能大于 256 MB。插入数据的流程如下:

  1. 服务端接收到插入请求后,将数据写入预写日志(WAL)。
  2. 当预写日志成功记录后,返回插入操作。
  3. 将数据写入可写缓冲区(mutable buffer)。

每个集合都有独立的可写缓冲区。每个可写缓冲区的容量上限是 128 MB。所有集合的可写缓冲区总容量上限由系统参数 insert_buffer_size 决定,默认是 1 GB。

insert

数据落盘

缓冲区中的数据落盘有三种触发机制:

定时触发

系统会定时触发落盘任务。定时间隔由系统参数 auto_flush_interval 决定,默认是 1 秒。

落盘操作的流程如下:

  1. 系统开辟一块新的可写缓冲区,用于容纳后续插入的数据。
  2. 系统将之前的可写缓冲区设为只读(immutable buffer)。
  3. 系统把只读缓冲区的数据写入磁盘,并将新数据段的描述信息写入元数据后端服务。

完成以上流程后,系统就成功创建了一个数据段(segment)。

客户端触发

由客户端调用 flush 接口触发落盘。

缓冲区达到上限触发

累积数据达到可写缓冲区的上限(128MB)会触发落盘操作。

每个分段的所有相关文件都被存放在以段 ID 命名的文件夹中,比如记录实体 ID 的 UID 文件、用于标记已被删除实体的 delete_docs 文件,以及用于快速查找实体的布隆过滤器(bloom-filter)文件。

段内数据文件请参考 分区和数据段 中的示意图。

数据合并

小数据段过多会导致查询性能低下。为了避免此问题,Milvus 会在需要的时候触发后台段合并任务,即把小数据段合并成新的数据段,并删除小数据段、更新元数据。其中,新数据段的大小不低于 index_file_size

合并操作的触发时机如下:

  • 启动服务时
  • 完成落盘任务后
  • 建索引前
  • 删除索引后

已经建立了索引的分段不会参与合并操作。

建立索引

未建立索引之前,Milvus 对集合的查询操作都是以暴力搜索(brute-force search)的方式完成的。为提高查询性能,你可以为集合建立合适的索引。索引建成后,每个数据段都会产生一个索引文件,此时元数据也会同步更新。

更多索引相关信息,请参考 向量索引

删除

删除集合

  1. 客户端调用 drop_collection 接口来删除一个集合。
  2. 服务端接收到请求后,仅在元数据中把该集合(包括它的分区和段)标记为删除状态。对于已标记为删除状态的集合,将无法再对其进行任何新操作(比如插入和查询)。
  3. 后台的清理任务将被标记为删除状态的集合(包括它的分区和段)从元数据中删除,然后将该集合的数据文件和文件夹从磁盘上删除。如果在删除操作之前已经有对该集合的操作正在执行,后台清理任务不会删除正在使用的段,直到操作完成。

删除分区

  1. 客户端调用 drop_partition 接口来删除一个分区。
  2. 服务端接收到请求后,仅在元数据中把该分区(包括它的段)标记为删除状态。
  3. 后台清理任务按照删除集合的流程来删除该分区和元数据。

删除实体

Milvus 为每个分段建立了一个 delete_docs 文件,用来记录被删除向量在段内的位置。

Milvus 使用布隆过滤器(bloom filter)来快速判断一个实体 ID 是否可能存在于某个分段中。因此,在每个分段下都创建了一个名为 bloom_filter 的文件。

删除实体的流程如下:

  1. 客户端调用 delete_entity_by_id 接口删除集合中的实体。
  2. 服务端接收到请求后,执行以下操作删除实体:

    • 如果该实体在插入缓冲区中,直接删除该实体。
    • 否则,根据每个分段的布隆过滤器判断该实体所处的分段,然后更新该分段的 delete_docs 以及 bloom_filter 文件。

数据段整理

查询一个分段时,Milvus 会将该分段的实体数据以及 delete_docs 文件读入内存。虽然被删除的实体不参与计算,但它们也会被读入内存。所以,一个分段中被删除的实体越多,浪费的内存资源和磁盘空间越多。为了减少此类不必要的资源消耗,Milvus 提供了数据段整理(compact)的操作,流程如下:

  1. 客户端调用 compact 接口。
  2. 服务端接收到请求后,根据 delete_docs 所记录的信息,将段内未被删除的实体写入一个新的分段,并把旧分段标记为删除状态。之后将由后台清理任务负责清理被标记为删除状态的分段。如果旧分段已建立索引,新分段产生之后会重建索引。

compact 操作会忽略被删除向量占比小于 10% 的分段。

数据读取

  1. 客户端调用 get_entity_by_id 接口读取原始实体数据。
  2. 服务端接收到请求后,通过布隆过滤器找到实体所在的段,返回该实体 ID 对应的数据。

浮点型向量在 Milvus 中以单精度(float)方式存储。