本文介绍了VCORFU,这是一个建立在共享日志上的高度一致的CloudScale对象存储。 VCORFU增强了共享日志的传统复制方案,以提供快速读取,并利用了一种新技术,即可组合状态机复制,将较小的状态机组合成较大的状态机,使状态机复制的使用能够有效地用于大型数据存储。 我们展示了VCorfu的性能优于Cassandra,后者是一种流行的最先进的NoSQL存储库,同时提供了强大的一致性(不透明性、读自写)、高效的事务处理和云规模的全局快照
VCorfu实现了一个共享日志抽象,它消除了共享日志的开销和限制,支持回放,而不会强制客户端回放潜在的无关更新。 VCORFU使用一种称为流物化的新技术来虚拟化日志。 Tango中的流仅仅是共享日志中的标记,与此不同,物化流是一种第一类抽象,它支持随机和批量读取,就像Kafka[26]和Kinesis[30]这样的分散日志一样,但具有像Corfu[9]和Tango[10]这样的共享日志的所有一致性好处。
VCORFU流存储体系结构如图2所示。 在VCORFU中,数据被写入到物化流中,数据条目在全局日志和单个流上从序列器服务器接收单调递增的令牌。 定序器可以有条件地发出令牌,以启用快速乐观的事务解析,如第4节所述。 VCORFU以更新的形式将数据写入日志副本和流副本,每个副本的索引不同。 这种设计复制数据是为了耐久性,但允许使用不同的密钥访问数据,类似于Replex[39]。 这样做的好处是,客户端只需联系流副本就可以直接读取流的最新版本。

A layout service maintains the mapping from log and
stream addresses to replicas. Log replicas and stream
replicas in vCorfu contain different sets of updates, as
shown in Figure 1. The log replicas store updates by their
(global) log address, and stream replicas by their stream
addresses. The replication protocol in vCorfu dynami-
cally builds replication chains based on the global log
offset, the streams which are written to, and the streams
offsets. Subsequent sections consider the design and implementation of materialized streams in more detail.
Layout service维护从日志和流地址到副本的映射。 VCORFU中的日志副本和流副本包含不同的更新集,如图1所示。 日志副本按其(全局)日志地址存储更新,流副本按其流地址存储更新。 VCORFU中的复制协议根据全局日志偏移量、写入流和流偏移量动态建立复制链。 随后的小节将更详细地讨论物化流的设计和实现

VCORFU具有弹性和可伸缩性:可以随时从系统中添加或删除副本。 由于序列器只是发出令牌,所以它不会成为I/O瓶颈。 重新配置只需改变活动布局即可触发。 最后,VCORFU是容错的–存储在VCORFU中的数据可以根据系统中副本的排列和数量容忍有限数量的故障,恢复处理类似于Replex[39]中的机制。 通常,只要日志副本和流副本不同时失败,VCORFU就可以容忍失败。 可以从日志副本的集合中重建流副本,并且可以通过扫描所有流副本来重建日志副本。
在操作上,流物化将单个全局日志划分为物化流,这些物化流支持日志操作:追加、随机和批量读取、修剪、检查和填充; 完整的API如表1所示。 每个物化流映射到VCORFU中的一个对象,每个流存储对该对象的修改的有序历史,遵循SMR[37]范式。
在VCORFU中,称为布局的映射描述了全局日志或给定物化流中的偏移如何映射到副本。 VCORFU客户端运行时必须获得最新布局的副本,以确定要与哪个副本交互。 每个版面都印有一个纪元号。 副本将拒绝来自具有陈旧时代的客户端的请求。 基于Paxos的协议[27]确保所有副本都同意当前的布局。 图3显示了一个示例布局。 布局类似于日志上的租约:具有错误布局(和错误的纪元号)的客户端请求将被副本拒绝。 该布局使客户端能够安全地直接联系流副本以获取流的最新更新。
附加到物化流(或多个流)的客户端首先获得当前布局,并用流ID向定序器发出请求。 定序器返回日志令牌和流令牌,日志令牌是全局日志中下一个地址的指针,流令牌是流中下一个地址的指针。 使用这些标记和布局,客户端确定要写入的副本集。
与传统设计不同,VCORFU中的副本集在追加过程中是动态排列的。 为了容错,每个条目被复制到两种副本类型上:第一种由日志中的地址索引(日志副本),第二种由流ID和流地址的组合索引(流副本)。 要执行写操作,客户端首先写入日志副本,然后写入流副本。 如果副本以前接受了对给定地址的写操作,则该写操作将被拒绝,客户端必须使用新的日志令牌重试。 一旦客户端写到两个副本,它就通过向它访问的每个副本广播一个提交消息来提交写(除了最后一个副本,因为最后一个写已经提交了)。 副本将只为提交的数据提供读取服务。 这使得流副本能够提供密集的物化流,没有漏洞。 客户机的写路径在正常操作中需要四次往返,如图4所示。 一个服务器驱动的变体,其中日志副本写到流副本接受6条消息; 我们把这个变体的实现留给以后的工作。


流副本实际上包含 任何给定流的所有更新,这意味着可以通过单个节点来读取流的全部内容。你知道,即使在全局日志上,它们也可能分散在多个副本中,我们将被迫一次读取流的一个条目。所以将所有更新都放在单个副本上,我们可以在一次往返中读取整个流,极大的提高了读取性能,特别是客户端需要读取很多条目的时候。
