全序广播
全序广播
如果你的程序只运行在单个
单主复制通过选择一个节点作为主库来确定操作的全序,并在主库的单个
全序广播通常被描述为在节点间交换消息的协议。非正式地讲,它要满足两个安全属性:
-
可靠交付(reliable delivery
) :没有消息丢失:如果消息被传递到一个节点,它将被传递到所有节点。 -
全序交付(totally ordered delivery
) :消息以相同的顺序传递给每个节点。
正确的全序广播算法必须始终保证可靠性和有序性,即使节点或网络出现故障。当然在网络中断的时候,消息是传不出去的,但是算法可以不断重试,以便在网络最终修复时,消息能及时通过并送达(当然它们必须仍然按照正确的顺序传递
使用全序广播
像
全序广播的一个重要表现是,顺序在消息送达时被固化:如果后续的消息已经送达,节点就不允许追溯地将(先前)消息插入顺序中的较早位置。这个事实使得全序广播比时间戳命令更强。考量全序广播的另一种方式是,这是一种创建日志的方式(如在复制日志,事务日志或预写式日志中
全序广播对于实现提供防护令牌的锁服务也很有用。每个获取锁的请求都作为一条消息追加到日志末尾,并且所有的消息都按它们在日志中出现的顺序依次编号。序列号可以当成防护令牌用,因为它是单调递增的。在
使用全序广播实现线性一致的存储
在线性一致的系统中,存在操作的全序。这是否意味着线性一致与全序广播一样?不尽然,但两者之间有者密切的联系。从形式上讲,线性一致读写寄存器是一个“更容易”的问题。全序广播等价于共识,而共识问题在异步的崩溃
全序广播是异步的:消息被保证以固定的顺序可靠地传送,但是不能保证消息何时被送达(所以一个接收者可能落后于其他接收者
设想对于每一个可能的用户名,你都可以有一个带有
-
在日志中追加一条消息,试探性地指明你要声明的用户名。
-
读日志,并等待你所附加的信息被回送。
-
检查是否有任何消息声称目标用户名的所有权。如果这些消息中的第一条就你自己的消息,那么你就成功了:你可以提交声称的用户名(也许是通过向日志追加另一条消息)并向客户端确认。如果所需用户名的第一条消息来自其他用户,则中止操作。
由于日志项是以相同顺序送达至所有节点,因此如果有多个并发写入,则所有节点会对最先到达者达成一致。选择冲突写入中的第一个作为胜利者,并中止后来者,以此确定所有节点对某个写入是提交还是中止达成一致。类似的方法可以在一个日志的基础上实现可序列化的多对象事务。尽管这一过程保证写入是线性一致的,但它并不保证读取也是线性一致的,如果你从与日志异步更新的存储中读取数据,结果可能是陈旧的
-
你可以通过追加一条消息,当消息回送时读取日志,执行实际的读取。消息在日志中的位置因此定义了读取发生的时间点
。 (etcd 的法定人数读取有些类似这种情况。 ) -
如果日志允许以线性一致的方式获取最新日志消息的位置,则可以查询该位置,等待直到该位置前的所有消息都传达到你,然后执行读取
。 (这是Zookeeper sync() 操作背后的思想) 。 -
你可以从同步更新的副本中进行读取,因此可以确保结果是最新的
。 (这种技术用于链式复制;参阅“复制研究”。 )
使用线性一致性存储实现全序广播
上一节介绍了如何从全序广播构建一个线性一致的
请注意,与兰伯特时间戳不同,通过自增线性一致性寄存器获得的数字形式上是一个没有间隙的序列。因此,如果一个节点已经发送了消息
这并非巧合:可以证明,线性一致的