摘要
云存储系统的三个指标: 高可靠性,低存储开销,高读写性能。
这三个指标是没有办法同一时候满足的,许多时候须要进行tradeoff。
副本系统和纠删码是两种在存储系统中广泛使用的策略,它们在保证高可靠性的前提下,选择了不同极端的tradeoff。 副本存储开销大,但性能较好。纠删码存储开销低。但性能较差。本文提出了MICS系统。它将一个对象以两种形式存储,一种採用副本。一种採用分片纠删码,不仅如此。还设计了针对这种hyprid结构的精细的读写协议。
在服务使用者的角度。MICS通过PRAM一致性协议保证高可用性。
实验方面。在I/O吞吐率上有所提高,同一时候在多种真实负载情况下,依旧表现出优越的性能。方差很小。
论文名字:MICS : Mingling Chained Storage Combining Replication and Erasure Coding
疑问:
为什么副本不适合写密集型应用。而纠删码适合?
感觉副本和纠删码在写密集型应用上的性能表现应该是一致的。假设採用三副本以及RS(4,2)编码,那么更新(改动)数据的开销,都须要更新三个块(副本是三个相同的块。纠删码是1个数据块加上两个校验块)。 我觉得这个写应该是追加写。假设是追加4个数据块的话,对于副本来说系统须要创建12个数据块才干够满足三副本的要求。而对于RS(4,2)编码,系统仅仅须要创建6个数据块就可以。因此更快。
简单介绍
MICS是对象存储系统,对于每个数据对象使用两部分进行存储,Master Node存储这个对象的完整副本,而纠删码节点(ECC)存储对象的编码分片。
对于一个数据对象,採用RS(k,m)编码,那么会生成k+m个分配。k个数据片,m的校验片。 这全部的数据会被分散存储到1+k+m个存储节点上,因此称这种编码为MICS(1,k,m)编码。
MICS实现了两种实际可行的机制:读写协议和一致性模型。
读写协议:
顺序读,顺序写。随机写
Master Node(MN)节点承担了绝大部分的读请求,ECC保证了在MN节点挂掉后依旧能够对外服务读;在訪问随机写方面,ECC通过Parity Logging机制以较低的网络和延迟实现了随机写的过程。
一致性模型:
本文採用了PARM一致性模型来提供有效的读写性能。
严格一致性
读出的数据始终为近期写入的数据。也就是说这次写入。后面的全部人的都能够正常读取,须要全局锁,在分布式网络环境不可能实现。
Sequencial一致性
全部使用者以相同的顺序看到对同一数据的操作,可是该顺序不一定是实时的。也就是说,假设有一组对元素x的更新操作。它不保证每次更新都会立马被随后的读取到上次的更新,可是保证先读到更新一定在前。后读到的更新一定在后。
因果一致性
对有因果关系,须要保证顺序,无因果关系的能够并行运行。无需关注它们的先后顺序。
Pipe random-access memory一致性
不同用户的写操作在不同的用户上来看是没有严格的顺序的,但同一个用户的写操作在不同的用户看来是有严格的顺序的。
说起来很拗口,简单举个样例: 用户A进行了A.1, A.2操作,用户B进行了B.1, B.2操作,那么在用户C他可能看到的操作有 A.1 A.2 B.1 B.2, A.1 B.1 B.2 A.2, B.1 B.2 A.1 A.2等等, 也就是说他看到的A的操作必须有A.1在A.2之前。B的操作有B.1在B.2之前,可是对于A和B的操作,他并没有得到严格的顺序。
借用wiki的一个样例:
P1:W(x)1
P2: R(x)1W(x)2
P3: R(x)1R(x)2
P4: R(x)2R(x)1
Time —->
上面的这个是符合PRAM一致性的,这是因为对于P3和P4来说。它们仅仅能看到P1的操作有顺序性。P2的操作有顺序性。可是并不能看到P1和P2的操作具有顺序性。
可是要注意的是,上面的样例不符合强一致性,顺序一致性(要求先读到的一定是先操作的,尽管不是实时的,P4进程不符合要求)。因果一致性(x的两个写入操作有因果关系,须要保证顺序性)
eventual一致性
在没有新的更新的情况下。先前的更新会终于同步到全部的节点。能够保证数据终于是一致的。而在更新的过程中。用户有可能读到陈旧的数据。
架构设计
系统架构例如以下图
本系统主要包括三部分,外部client,代理服务器PSC,以及底层对象存储池OSC。
client
前端webserver,三种操作upload, download, update,也就是说顺序写。读。随机写。每个请求的头包括例如以下字段:
client_id, req_type, node_id, obj_id, req_ts
PSC
几个高性能机器,维持全局一致性。PSC负责将用户的请求导向相应的存储节点。
OSC
包括10-1000台的存储机器集群,每一台节点负责管理它上面的对象的metadata。
obj_id, obj_meta
写协议
顺序写
首先到达Master Node,然后这个object被成功存储之后,MN节点对这个对象进行分片,并将数据和校验分片散步到ECC中多个节点上,每个ECC节点在存储完成之后向MN返回ACK,MN在收到全部ECC分片的ACK之后回应client。同一时候每个节点都在mem_table中插入下面条目:
obj_id, data_type, version_history
数据类型记录了分片的位置信息,版本号历史是一个链表记录了此对象的历史更新。它包括一堆entry,每个entry就是一个version,包括client_id, req_ts, offset(初始上传之后。此处offset为0),delta_block,tag(标记数据是clean还是dirty)。
也就是说在对象第一个创建之后,对于这一个对象会生成一个这种条目。
它的key就是对象的id。value是数据的类型以及版本号历史。刚创建之后会插入第一个版本号。即offset=0的版本号。
随机写:
採用parity-logging算法更新。
1,当数据更新的请求到达之后,它首先被PSC导向到更新位置相应的ECC的数据节点上(注意此处并不会先到达MN节点),当前数据节点立马用新的数据覆盖。并计算出一个delta_block。同一时候会构建一个新的version,此时version的tag为dirty数据。
2,新的version和delta_block会被发送给其它的校验ECC节点,而单独的version会被发送给MN节点。 全部的节点会将这个version插入到相应的obj_id的version_history中。
3。校验ECC节点在收到delta_block后。会将其增加到version相应delta_block条目中(当然事实上就是一个指针而已)。delta_block仅仅会放入到一个buffer中,然后将指针赋值给version的条目就可以。
4。被更新的数据节点在收到全部校验节点的ACK之后,就会回应client更新成功。然后通知MN发起reconstruct数据。
5。MN在收到重构请求后,開始从全部的ECC数据节点并行读取分片,并构建对象,存储对象。然后将第2步中收到的version置位为clean。
6,最后MN也通知其它的ECC节点将version的tag改为clean,校验节点将先前存储的delta_block删除就可以。
详细例如以下图:
读协议
正常读
Rnor,MN节点工作正常,具有相应的数据,能够直接读取就可以。
轻量failure读
Rlwf, MN节点没有相应的数据,而全部的数据节点能够正常回应。全部的ECC数据节点将内容assemble在一起传递给client就可以。 同一时候。也会像Mn发送重建请求并由MN决定是否发起对象重建。
假设MN仅仅是因为临时的延迟导致没有回应,那么MN就不会发送重建;假设数据确实故障了,须要发起重建请求。进行重建操作。
严重failure读
Rhf, 当MN以及至少一个data_node不能正常回应后,须要启动数据重建解码过程。
当解码完成后。数据返回给client。 同一时候重建请求也会发送给MN以及failed data_nodes来让它们决定是否发起数据重建操作。
PRAM一致性模型
假设read请求的req_ts是强制的,那么返回的对象版本号一定不能比req_ts旧。
1. 对于R_nor这种强制请求,返回对象的最新版本号。
2. 对于R_nor这种非强制请求,从版本号历史中寻找一个合适的版本号返回:
1. 假设最新版本号是clean的,那么直接返回。
2. 假设最新版本号是dirty的,MICS向前扫描version_history直到下面某一个条件满足:(a)obj_id是请求的对象id, (b)当前的版本号是clean的,(c)找到开头。
MICS然后返回给client,它要不从磁盘中读取。要不使用delta_block去重构它。
3. 对于R_lwf和R_hf。 MICS重建最新的版本号并返回给client,parity_nodes将全部的delta_blocks进行合并到校验块中,并删除除了最后一个的delta_blocks.
再增加一个一致性的图
实现不再描写叙述了。
关于一致性的问题:
upload创建数据对象。直到数据对象全然保存好。才返回给client。能够保证一致性。
update更新对象在全部的校验节点收到delta_block就会立马返回client更新成功。 它怎样保持的PRAM一致性的? 当单个client发起连续的两个请求,仅仅有在第一个请求的全部delta_block全然写入到相应的校验节点上,才会返回给client,这样第二个请求才干够继续发起。
通过这种方式,两个delta_block肯定具有着先后顺序的,当其它用户发起读请求后,一定仅仅会先读到第一个请求的更新,再读到第二个更新。也就是说不同的客户的看到单独的某一个client的请求具有顺序性。
当有多个client同一时候发起请求后,比如A和B,它们同一时候发起update(x)的请求。这两个请求来源于不同的client,因为网络传输的不同,有些parity节点会先收到A的更新。有些parity节点会先收到B的更新,这样在pairty仅仅收到一个节点更新的情况下,有外部的其它的client来读。它们就有可能命中到先收到A的parity节点从而读取到A,也有可能从其它parity节点读取到B,也就是说不同的client看到不同的client的请求不具有顺序性,可是全部的parity节点终于都会收到A和B的请求,尽管它们到来的顺序不同。最后这些delta_block会在相应的parity节点上依照req_ts进行排序。从而保证终于一致性。
关于更新的磁盘开销。
4副本和MICS(1,4,2)对照: 4*4 / (4+1+2) = 2.33x
4副本和RS(6,3)对照: 4*6 / (1+3) = 6x