时间同步
本主题介绍了 Milvus 中的时间同步机制。
概述
Milvus 中的事件通常可以分为两类:
-
数据定义语言(DDL)事件:创建/删除 Collection,创建/删除 Partition,等等。
-
数据操作语言(DML)事件:插入、搜索,等等。
任何事件,无论是 DDL 还是 DML 事件,都标有一个时间戳,可以指示此事件何时发生。
假设有两个用户在 Milvus 中按下表所示的时间顺序启动一系列 DML 和 DDL 事件。
时间戳 | 用户 1 | 用户 2 |
---|---|---|
t0 | 创建了一个名为 C0 的 Collection。 | / |
t2 | / | 在 Collection C0 上进行搜索。 |
t5 | 向 Collection C0 中插入数据 A1 。 | / |
t7 | / | 在 Collection C0 上进行搜索。 |
t10 | 向 Collection C0 中插入数据 A2 。 | / |
t12 | / | 在 Collection C0 上进行搜索 |
t15 | 从 Collection C0 中删除数据 A1 。 | / |
t17 | / | 在 Collection C0 上进行搜索 |
理想情况下,用户 2 应该能够看到:
-
在
t2
时,一个空的 CollectionC0
。 -
在
t7
时,数据A1
。 -
在
t12
时,数据A1
和A2
。 -
在
t17
时,只有数据A2
(因为数据A1
在此时间点之前已从 Collection 中删除)。
当只有一个单一节点时,这种理想情况很容易实现。但是,Milvus 是一个分布式向量数据库,为了确保不同节点中的所有 DML 和 DDL 操作都保持有序,Milvus 需要解决以下两个问题:
-
如果上述示例中的两个用户在不同的节点上,他们的时间时钟是不同的。例如,如果用户 2 比用户 1 晚 24 小时,那么用户 1 的所有操作对用户 2 来说都是不可见的,直到第二天。
-
可能存在网络延迟。如果用户 2 在
t17
时对 CollectionC0
进行搜索,Milvus 应该能够保证t17
之前的所有操作都已成功处理和完成。如果t15
的删除操作由于网络延迟而延迟,用户 2 在t17
进行搜索时很可能仍然能看到本应被删除的数据A1
。
因此,Milvus 采用时间同步系统(timetick)来解决这些问题。
时间戳预言机(TSO)
为了解决前一节中提到的第一个问题,Milvus 像其他分布式系统一样,提供了时间戳预言机(TSO)服务。这意味着 Milvus 中的所有事件都必须从 TSO 而不是从本地时钟分配时间戳。
TSO 服务由 Milvus 中的 root coordinator 提供。客户端可以在单个时间戳分配请求中分配一个或多个时间戳。
TSO 时间戳是一种 uint64
值类型,由物理部分和逻辑部分组成。下图演示了时间戳的格式。
.
如图所示,开头的 46 位是物理部分,即以毫秒为单位的 UTC 时间。最后 18 位是逻辑部分。
时间同步系统(timetick)
本节使用数据插入操作的示例来解释 Milvus 中的时间同步机制。
当 proxy 从 SDK 接收到数据插入请求时,它根据主键的哈希值将插入消息分配到不同的消息流(MsgStream
)中。
每个插入消息(InsertMsg
)在发送到 MsgStream
之前都会被分配一个时间戳。
MsgStream
是消息队列的包装器,在 Milvus 2.0 中默认为 Pulsar。
一个通用原则是,在 MsgStream
中,来自同一个 proxy 的 InsertMsgs
的时间戳必须是递增的。但是,对于来自不同 proxy 的 InsertMsgs
,没有这样的规则。
下图是 MsgStream
中 InsertMsgs
的示例。该片段包含五个 InsertMsgs
,其中三个来自 Proxy1
,其余来自 Proxy2
。
来自 Proxy1
的三个 InsertMsgs
的时间戳是递增的,来自 Proxy2
的两个 InsertMsgs
也是如此。但是,Proxy1
和 Proxy2
的 InsertMsgs
之间没有特定的顺序。
一种可能的情况是,当从 Proxy2
读取时间戳为 110
的消息时,Milvus 发现来自 Proxy1
的时间戳为 80
的消息仍在 MsgStream
中。因此,Milvus 引入了时间同步系统 timetick,以确保当从 MsgStream
读取消息时,所有具有较小时间戳值的消息都必须被消费。
如上图所示,
-
每个 proxy 定期(默认每 200 毫秒)向 root coord 报告
MsgStream
中最新InsertMsg
的最大时间戳值。 -
Root coord 识别此
Msgstream
上的最小时间戳值,无论InsertMsgs
属于哪个 proxy。然后 root coord 将此最小时间戳插入到Msgstream
中。此时间戳也称为 timetick。 -
当消费者组件读取 root coord 插入的 timetick 时,它们了解所有具有较小时间戳值的插入消息都已被消费。因此,相关请求可以安全执行而不会中断顺序。
下图是插入了 timetick 的 Msgstream
示例。
MsgStream
根据时间戳批量处理消息,以确保输出消息满足时间戳的要求。在上面的示例中,它将消费除了 Proxy2
在 Timestamp: 120
的 InsertMsgs
之外的所有记录,因为它在最新的 TimeTick 之后。