1. 云计算存储发展
2003年Google 发表 paper GFS,揭示了其解决其索引这个星球巨大规模的互联网数据的存储问题,2006年Amazon推出划时代的AWS云计算服务EC2和S3,开启了改变世界IT格局的云计算时代。Google、微软、阿里云等等都随之入局。
上文A Bite os S3 Arch 谈到了如何构建分布式存储系统来支持大规模的S3对象存储问题。而云计算还有很重要的另外2大基础存储基础设施组件EBS(Elastic Block Storage)、EFS(Elastic File Service),对应于传统IT基础设施中的本地盘和共享文件存储业务。
对于云计算来说很重要的一点是超售和弹性,所以支撑EBS和EFS方案的底层基础存储层的支撑也不太可能是local的本地化方案,必须是分布式的存储资源管理和分配系统。
但是相比于S3业务,EBS底层的分布式存储系统对外提供的IO模型其实非常的不同。 S3对外基本提供接口能力为 PUT/GET/DELETE,对底层分布式文件系统的需求是Append。而EBS、EFS 对用户是需要支持随机地址空间的改写,那么如何设计底层存储系统来支持这2类业务?
2. 现状对于这块的业务架构,当前各大互联网公司基本没有开源,基本的架构也没有或者很少正面出来说的。这一方面可能算是商业机密,另一方面也可能是从安全和舆论等层面的考量。
当然开源系统和一些互联网公司的存储系统的paper是可以参考的,比如CEPH、Lustre、HDFS、GlusterFS、FastDFS、Window Azure Storage、GFS等等。这些都自称为分布式文件系统,但具体到场景又有很多不同,对文件的定义也是各异,如下几大方面是非常的不同点
EBS底层的分布式存储系统,需要至少保证如下几点
EBS底层分布式存储系统需要向上提供如上的几点重要的特性之外,还有很重要的一些核心需求
那么如何设计和架构EBS底层的分布式存储系统来满足业务的需求,也就是如何设计向上层业务提供随机IO能力的分布式存储系统。下面分析下几种可行的构建方式。
3 EBS 构建3.1 构建方式一对于EBS业务来说,抽象上来说需要底层分布式文件系统提供可随机读写的文件的能力,按照最直接的方式,可以有如下图所示的逻辑映射关系,1TB Vdisk /dev/sad对应于底层分布式文件系统为 /foo/sda 这样一个文件,1TB地址空间上的chunk是按需分配的,分配的基本单元一般来说是固定的,如下图的4MB。
qemu等提供了对接块设备driver的API,只要在client一侧对应分布式文件系统对接实现这样的API即可向上输出块设备。
在网易从0到1实现了这种构建方式CURVE 目前已经开源(百度也是类似这种方案),底层抽象为一典型的分布式文件系统,向业务输出随机可读写能力的文件以支撑上层块设备EBS类型业务。基本架构类似于经典的分布式文件系统的鼻祖GFS,如下图所示。
基础架构
数据面核心
底层核心ChunkServer之间是基于一致性协议(Raft) stateMachine的方式实现副本一致性,详见Raft peer Replicated State Machines 章节。所有的上层用户的写入依赖于一致性协议实现多副本的一致性保证,每个用户的Chunk被分配到这些复制组中。
如上图所示
此种架构方案优点:
此种方案的缺点:
如上构建方式1是从0到1构架设计和实现。第2种构建方式主要是跟圈内的童鞋交流方案,细节无法进行的考究,但是从总体架构层面进行说明和分析没有什么大的问题。
其基本架构类似于Window Azure Storage的架构体系。在底层构建Stream Layer,在Stream Layer(类似GFS)的基础上构建Partition layer,在Partition Layer(类似BigTable)的基础上可以构建EBS方案。
如何基于这样的架构支持业务,需要换一个角度来思考如何解决这个问题,回到问题的出发点,EBS随机地址空间读写的方案核心是解决什么问题?
块设备一般来说提供了原子粒度的IO保障,比如磁盘一般来说保障512Byte的原子读写;一般SSD提供4KB级别的原子读写特性。
简单来说,块设备提供了对每一个地址空间(比如4KB)的原子写入和读取(PUT/GET)。换一个角度来说如果对每一个EBS实例的每一个4KB内容定义一个全局可编码的唯一Key,其内容为4KB的固定大小的Value。那么这个底层分布式存储系统就是一个专用的分布式Key-Value系统。
构建方式2基本就是遵循这样一个思路来构建的。
如上为基本的逻辑视图示例,底层的物理层大构建是如下图所示。
StreamLayer:类似GFS/HDFS层次,每一个stream 类似一个文件,stream 只支持append。stream 由一个个extent组成(类似 HDFS/GFS 的chunk),extent使用固定复制组的方案实现多副本/EC(参考微软PacificA 协议)。extent支持可变长度,在extent 不可用的通过seal/new的方式新建一个extent继续提供写入,保障写入的高可用。
Partition Server:提供table的某个区段(按照字典序分裂)的kv 存储服务(增/删/改/查)。其通过WAL实现数据的快速和及时写入,保障数据的持久性;然后写入MemStore(SkipList跳表实现),在MemStore达到固定大小,比如128MB的时候Dump成SSTable(Sorted String Table),Block Cache 负责SStable 热点数据的读缓存。一个Partiton Server 负责多个Partition。
架构方面的更加具体的参考Window Azure Storage 的paper或者BigTable开源实现方面的书籍和资料,这里不多做展开。
在实际操作层面,对于一个上层的EFS实例,有挺多的映射关系:
一般来说为了实现EFS的快照和克隆功能,一个EFS实例一般都选择一个table。如果是只有一个WAL,写入的吞吐容易受影响,可以在上层使用一定的间接的业务逻辑手段使得多个相邻的地址空间换分到不同的Region,从而把上层IO映射到更多的Region,使用多个WAL写入能力提升带宽。比如将地址空间按照1MB取模划分到4个独立的连续字典序区间Region。
此种构架方案的优点:
反脆弱:这是这种方案我认为最最大的优点,也是相比方案一的绝对杀手锏,很多分布式存储系统其实从 基层层面都没有好好考虑这个问题,或者到意识到这种方案的巨大优势。即从读层面WAS基本实现了 已经seal 数据的任何副本是可读的。对于写数据来说,由于写入只限制在当前 尚未seal 掉的chunk上,也就是3个磁盘上,并且在故障情况下随时可以seal掉转移。这两点使得虽然 上层一个table的数据打散到底层这么多磁盘上。但是从爆炸半径来说,基本上算是实现了本地盘方案的爆炸半径,甚至在故障,过载情况下可以实现快速转移。从这点上来说,这样基本为分布式存储方案去完全干掉本地存储方案,打下了极其坚实的基础。
此种构建方案的缺点:
构建方案3类似方案2的变种。核心的不同点在于,构建方案2的全局排序和垃圾回收工作依赖于后期的compaction。而构建方式3倾向于尽可能块得进行原地排序。
其基本原理是,通过WAL 写入确保数据持久性,然后应用到内存。后台定期将写入原地应用到随机读写文件(没错,底层除了需要实现AppendFile,还需要实现弱一致的随机读写文件RandomFile,多幅本的数据一致性由Client负责,这引入一个缺点,随机读写文件很难做EC),WAL使用seal-new的方式确保写入可用性,读取采用Merge 更新数据(wal) 基线数据(随机读写文件数据)的方式实现数据的读取。在内存中建立对整个WAL内容的索引,最新的数据在WAL中则直接从WAL中读取,否则从随机读写文件中读取,其基本读写流程图如下
写入:写入还是一样写入WAL,然后更新内存索引,将最新数据的使用MemTable(Hash表)指向WAL。但是最终结果的Dump其实并不是按照sstable的方式,而是直接后台异步dump到对应RandomFile的随机地址空间。
PS1:WAL 也是类似Segment方式,定期做checkpoint(刷入RandomFile),在内存中会构建所有WAL segment的索引,WAL采用Seal/New的方式来保障可用性。
PS2: RandomFile是随机读写文件,为了保障WAL长度的收敛性,后台线程会将WAL数据写入RandomFile。RandomFile 没法使用Seal-New的可用性,但是由于是BackGround的写入。所以如果发现一个副本失败,会使得BackGround dump机制阻塞(这种方案在坏盘恢复场景下,会导致BackGround写入会卡很久)。
读取: 读取会首先通过Memtable查找最新数据是否在WAL中,不存在的情况下再读RandomFile。对于RandomFile未达到一致性状态的数据,会从WAL里头进行读取。(PS:由于WAL(AppendFile) 到 RandomFile的写入是全三副本,并且一直是卡着的,所以RandomFile的恢复可以采用很鲁棒的方式,直接选中任意一个还在复制组中的副本作为数据源复制。)
这种方案可以一定程度解决。读放大、空间利用率等方面的问题。但是在克隆、快照等方面会稍微差一些,并且随机读写文件很难做EC(EC改写好麻烦)。
4. 总结如上对3种构建方式进行了简要的构建和优缺点的说明。没有绝对的那个构建方案优于另外一个构建方案。在实践过程中应结合自身面向的实际业务特点和技术条件等进行选择,能快速跟上业务发展并且对方案做中长期的评估适时进行调整。
很重要一点是一定得进行抽丝剥茧、形而上得分析问题的本质。这样才能够把精力集中在真正的问题上,好钢放在刀刃上。
简单来说,块设备提供了对每一个地址空间(比如4KB)的原子写入和读取(PUT/GET)。换一个角度来说如果对每一个EBS实例的每一个4KB内容定义一个全局可编码的唯一Key,其内容为4KB的固定大小的Value。那么这个底层分布式存储系统就是一个专用的分布式Key-Value系统。
以上不同的方案,从逻辑上往上抽象都可以认为是向业务提供KV存储。最本质的区别可以认为是排序在什么时候进行。构建方案1总体来说是即刻全局排序。构建方案2 则采用局部有序的方案。而架构方案3则 使用架构方案1和方案2 的折中。
另外一个很重要的差别是从核心可靠性的角度来说,构建方案1采用了多数派(如Raft)方案应对分布式的CAP问题。而构建方案2、3采用PrimayBackup Master(多数派) Append Only的特点来应对CAP问题。
Notes作者:网易存储团队工程师 TOM。限于作者水平,难免有理解和描述上有疏漏或者错误的地方,欢迎共同交流;部分参考已经在正文和参考文献中列表注明,但仍有可能有疏漏的地方,有任何侵权或者不明确的地方,欢迎指出,必定及时更正或者删除;文章供于学习交流,转载注明出处
5 参考文献