分布式存储有块存储、对象存储、文件存储,有不同的开源项目如Ceph、GlusterFS、Sheepdog、Swift,还有不同的商业实现如Google、AWS、微软、金山、七牛、又拍、阿里云还有Qingcloud等,思路或多或少都有些不同,可选的硬件种类也很多。似乎可选的东西太多了,而且各有优缺点。
选择的多样性是好事,同时也对技术选型人员的知识和能力提出了更高的要求。我们提出了有关分布式存储的三个基本问题,邀请领域内各路专家来分享他们的解读。
三个基本问题分别是:
对于一套分布式存储的方案,怎样评估它是好还是不好?
如何对分布式存储的不同实现进行分类?
分布式存储中的“数据可靠性”是如何计算的?
InfoQ:无论是在做方案的选型,还是实现一套存储系统,权衡的因素主要考虑哪些?有没有一套比较全面的考量/评估方式来决定哪种存储方案更适合哪种场景?换句话说:当你去看一个存储方案的实现,无论是看代码还是实测,你如何评估这套方案哪里好,哪里不好?
甘泉:
其实评价一个存储方案好和不好,只有一个客观的标准,那就是是否能满足用户的需求。不站在这个立场上思考这个问题,就会将技术凌驾于用户需求之上,而技术应该是服务于用户需求的。
那么我们可以简单的剖析一下用户对存储的需求到底是什么。从青云的角度来说,有以下几点是用户需要的:
运行或在线系统需要高性能。这个不用多说,用户的业务数据几乎都会存储在数据库里面,没有一个用户会觉得数据库性能不重要
离线或备份数据需要高容量,低价格。这部分数据通常量很大,但是对性能要求不高,对不经常用的东西也不希望负担高额成本。
所有的数据都必须是可靠的,绝对不能丢。这是用户对于存储的容忍底线,所有负责任的企业都会将可靠性放在最重要的位置上,在这个基础上能达到多高的性能就看技术实力了。
所以从上面的分析来看,之所以没有银弹方案,是因为用户对存储的需求差异很大,不同的需求需要用不同的方式来解决。这就好像现在机械 硬盘已经存在这么多年了,磁带依然没有消失的原因,因为它用一种最廉价的方式解决了大容量离线数据的存储问题,虽然它是最慢的。
王旭:
首先对象存储和文件存储的区别是不大的,存储的都是一样的东西,只是抛弃了统一的命名空间和目录树的结构,使得扩展起来桎梏少一些。
独立的互联网存储服务一般都是做对象存储的,因为块存储是给计算机用的,对象存储是给浏览器等HTTP客户端用的。独立服务所提供的存储系统,访问都来自互联网,自然是做对象存储;与之相对应,大部分类AWS的主机服务商都会提供一个块存储服务搭配主机服务。
同一个服务商同时提供两个服务是有好处的,除了提供的服务比较全这个优点以外,对象存储还可以支撑块存储的快照、主机的系统镜像存储等应用,可以相互结合的。
权衡的因素有很多——可靠性要求、可用性要求、时延要求、一致性要求、使用模式相关要求(包括请求大小、QPS/IOPS、吞吐)等。
比如:
对于块存储,要求的访问时延是 10ms 级的,因为给虚拟机用的,传统硬盘也是10ms 级的时延,请求尺寸都很小,但qps(iops)可能会很高,那么在这种情况下:
异地多中心是不现实的,存储要和主机尽量接近,相应地可靠性必然会有所打折
强一致副本不会过多,强一致要求对时延有影响
对于对象存储,要求的访问时延是 100ms - 1s 级的,请求一般是中到大尺寸,低 qps 的,在这种情况下
可以用更多的分散副本数来换取更高的可靠性,但过多副本增加维持一致性的难度,需要折衷
另外SSD随着成本降低,在块存储里逐渐成为主流了,以便提供更好的IOPS,AWS这个月开始,创建的EBS卷缺省就是SSD的了。
对于评价一个实现,首先是看适合不适合这个用途,然后看这个方案有没有显著的缺点,是否有严重的影响,然后成本之类的也是一个因素,做软件的人总觉的用便宜硬件实现高大上的服务才值得吹牛,呵呵。
我得补充一点就是,做存储这东西是需要良心的,一致性这种东西有的时候用户是很难看出来的,偶尔一个程序出错了,你一般不会怀疑是硬盘上存着的数据坏了,做存储服务的人还是要尽量避免出现这种情况的。底层存储服务(有别于数据库)的一致性是一种很难被用户观测到的,但是如果一个实现根本没达到应有的一致性,比如块服务,只写了一个副本就返回给应用说写成功了,这样是不太道德的,反正我个人坚持应该(EBS块存储)应该写两个副本再返回,在这个约束之内来优化。
也就是说,我的观点是,先看是否满足约束,然后看架构是否恰当,最后看细节流程的优化。
InfoQ:目前分布式存储从应用场景、实现等层面来看,是如何分类的?
王豪迈:
分布式存储的应用场景相对于其存储接口,现在流行分为三种:
对象存储: 也就是通常意义的键值存储,其接口就是简单的GET、PUT、DEL和其他扩展,如七牛、又拍、Swift、S3
块存储: 这种接口通常以QEMU Driver或者Kernel Module的方式存在,这种接口需要实现Linux的Block Device的接口或者QEMU提供的Block Driver接口,如Sheepdog,AWS的EBS,青云的云硬盘和阿里云的盘古系统,还有Ceph的RBD(RBD是Ceph面向块存储的接口)
文件存储: 通常意义是支持POSIX接口,它跟传统的文件系统如Ext4是一个类型的,但区别在于分布式存储提供了并行化的能力,如Ceph的CephFS(CephFS是Ceph面向文件存储的接口),但是有时候又会把GFS,HDFS这种非POSIX接口的类文件存储接口归入此类。
按照这三种接口和其应用场景,很容易了解这三种类型的IO特点,括号里代表了它在非分布式情况下的对应:
对象存储(键值数据库):接口简单,一个对象我们可以看成一个文件,只能全写全读,通常以大文件为主,要求足够的IO带宽。
块存储(硬盘):它的IO特点与传统的硬盘是一致的,一个硬盘应该是能面向通用需求的,即能应付大文件读写,也能处理好小文件读写。但是硬盘的特点是容量大,热点明显。因此块存储主要可以应付热点问题。另外,块存储要求的延迟是最低的。
文件存储(文件系统):支持文件存储的接口的系统设计跟传统本地文件系统如Ext4这种的特点和难点是一致的,它比块存储具有更丰富的接口,需要考虑目录、文件属性等支持,实现一个支持并行化的文件存储应该是最困难的。但像HDFS、GFS这种自己定义标准的系统,可以通过根据实现来定义接口,会容易一点。
因此,这三种接口分别以非分布式情况下的键值数据库、硬盘和文件系统的IO特点来对应即可。至于冷热、快慢、大小文件而言更接近于业务。但是因为存储系统是通用化实现,通常来说,需要尽量满足各种需求,而接口定义已经一定意义上就砍去了一些需求,如对象存储会以冷存储、大文件为主。
实现方面,主要有两层区别:
系统的分布式设计:主从、还是全分布式或者是兼而有之,目前现在存储系统因为一致性的要求,以主从为主。
底层的单机存储:一种是依赖本地文件系统的接口,如GlusterFS,Swift,Sheepdog,Ceph。一种是依赖块接口的,目前只知道Nutanix是使用这个的。最后一种是依赖键值接口的,目前应该只有Ceph是支持(Ceph支持多种单机存储接口)。第一种依赖文件系统是因为分布式存储系统本身已经够复杂,实现者很难从上层一直到底层存储都去实现,而本地文件系统已经是一个通用化并且非常成熟的实现,因此分布式存储系统绝大部分(上述提到的都应该是)都会直接依赖本地文件系统。第二种接口目前只知道Nutanix是支持的(传统的存储厂商的存储产品一般会使用这种方式),这种接口也就是比第一种去掉了文件系统层,实现一个简单的物理块管理即可。第三种它的主要原因是“存储定义”和对象存储的普及,希望硬盘来提供简单的键值接口即可,如希捷的Kinetic API,Fusionio NVMKV,这种接口另一方面是闪存厂商非常喜爱的,因为闪存的物理特性使得它支持键值接口比快接口容易得多,目前Ceph是支持这种接口,而希捷和华为最近推出了IP硬盘,听说已经实现了Swift上的原型。
从这里可以发现,分布式存储系统是在传统的单机接口上重新包装了一层,然后实现三种类似的接口。
策略方面,三副本、多AZ六副本和网络RAID都是一类的,它们都是指数据的分布策略来提供数据可用性,通常来说前两者情况就是数据的多个副本分布在所有服务器的几个中,也就是只要超过副本数的服务器挂掉,存储系统就面临部分数据不可用的情况。网络RAID是为了避免这种情况,比如在1000台服务器的情况,将其分成10台一组的100组,这样同样是一份数据(Data1)的三个副本都只属于某一个组,它不可用只当1组内(10台)中超过3个台机器不可用时才会发生,这样概率会小非常多。
EC是一个类副本策略,它可以理解为增强版的复制,更少的副本可以达到更好的数据可用。
硬件方面,SSD,SAS,SATA和内存的组合是为了提供数据访问的性能。千兆、万兆甚至Inifiniband是组合是为了提供网络传输的性能。
许式伟:
如果我们按存储的业务逻辑分,那么可以分为:键值存储(Key-Value Storage)、文件系统(File System)、数据库(Database,不是很严谨,只是作为所有支持多键值的存储的统称)、消息队列(MQ)等等。按这种分类方法存储的种类是无穷尽的,我曾经说过 “存储即数据结构”,表达的就是这个意思。数据结构无穷尽,存储也会无法穷尽。块存储我们很少把它划分到上面的分类中,因为它是虚拟存储设备,是为 VM 服务的一个东西,并不直接面向用户业务(用户不需要调用块存储设备的 api 来完成业务,当然如果我们把 VM 也认为是一个业务系统的话,块存储也勉强可以作为一种特定的数据结构存在)。对象存储(Object Storage)是键值存储(Key-Value Storage)的特例,它假设Value是文件,尺寸范围可以很大(比如七牛可以支持文件大小从0字节到1TB)。如果要对对象存储做进一步分类,我能够想到的就是按实现方案来分类。比如按冗余方案来分,可分为多副本、RAID、纠删码等等;或者我们按一致性方案分,可以分为主从结构和对等结构等。
不会有什么存储方案能够一统天下。不同业务系统的场景需求不同,对存储的诉求会不同,选择自然会不同。基于纠删码的存储系统,复杂性远高于三副本的存储系统。从易维护的角度,如果业务对存储成本不敏感,那么人们自然而然会选择三副本的方案。
王旭:
除了开源服务外,这些私有的实现都没有太公开自己的实现,不同的实现确实差异很大。但另一方面因为有共同的目标,常常有很多细节是做得差不多的,比如很多对象存储都会把小对象放到一些预分配的大块存储区域里,然后定期做compaction,来提高存储效率、避免文件系统碎片等。
对于块服务,有些实现是比较简单直接的,直接把存储分成块,用户有申请,就调度到某几台机器上,分配一些块,组成卷给用户用,而有些实现是比较高层的,会先构建一个底层的分部式存储系统,然后再从中分配块给用户用。后者技术含量更高一些,但一般前者的实现都属于简单直接型,实际效果反而更好(性能、故障处理等)一些。
InfoQ:所有做云存储的都表示数据丢失是不可接受的,但数据丢失的概率——即理论上的数据可靠性(reliability),不同方案的计算方式是不同的。你们是怎么做的,或者现在有哪些比较成熟的资料描述这个计算方法的?
王豪迈:
实际上这个计算是需要依赖于存储系统本身的。我们使用Ceph,Ceph的优势是提供了一个叫CRush算法的实现,可以轻松根据需要来规划数据的副本数和高可用性。参考Ceph提供的模型定义来规划自己的。这是我的同事朱荣泽做的故障计算,这个计算只针对副本策略,并不适合使用EC(擦除码)的情况。
硬盘发生故障的概率是符合泊松分布的。
fit = failures in time = 1/MTTF ~= 1/MTBF = AFR/(24*365)
事件概率 Pn(λ,t) = (λt)n e-λt / n!
我们对丢失数据是不能容忍的,所以只计算丢失数据的概率,不计算丢失每个object的概率。
N代表OSD的个数
R代表副本数
S代表scatter width,关系着recovery时间
我们忽略Non-Recoverable Errors的概率
计算1年内任意R个OSD发生相关故障概率的方法是
1年内OSD故障的概率。
在recovery时(R-1)个OSD发生故障的概率。
以上概率相乘。假设结果是Pr
因为任意R个OSD不一定属于Ceph的Copy Sets,则Ceph的丢失Copy Sets的概率是:
M = Copy Sets Number
在N个OSD中,任意R个OSD的组合数是 C(R,N)
丢失Copy Sets的概率是 Pr * M / C(R,N)。
最终公式是:
P = func(N,R,S,AFR)
许式伟:
可靠性的定义可以有不同,比如有人会定义为:假设整个系统有 L 个对象,在 1 年内会损失 m 个对象,那么可靠性为 1 - m/L。我在我那篇文章中的定义是:整个系统有 L 块硬盘,1 年内丢失数据的概率是多少(而不管丢失了多少个对象)。
沿用文章中的可靠性定义,数据可靠性的计算涉及到以下几个量:
集群规模-总硬盘数目(L)
容错度(M)
修复速度(t)
单盘可靠性(p:在t时间内损坏的概率)
我的计算方法是,先计算这L块硬盘在t时间内同时损坏M+1块硬盘的概率是多少(Pt,也就是丢失数据的概率,当然有细心的网友说t时间内同时损坏M+1块盘不一定会丢失数据,这点当然是正确的,但是这个丢失数据的概率虽然不为1但是非常接近1,而且此时对软件系统来说已经是失控状态,为了简化计算假设为1),然后把时间拉长到1年(T)的数据丢失概率(P)。这个拉长是非常简单换算公式:
P = 1 - (1 - Pt)T/t ≈ Pt * (T/t)
所以关键是计算 Pt(这L块硬盘在t时间内同时损坏M+1块硬盘的概率)。我们扩展一下,用 Pt(i) 表示 t 时间内有且仅有 i 块盘损坏的概率。前面的 Pt 实际上是 Pt(>M),而不是 Pt(M+1)。不难得出:
Pt(>M) = 1 - Pt(0) - Pt(1) - … - Pt(M)
好了,我们就剩下计算 Pt(i) 了。这个概率的计算比较常规:
Pt(i) = C(L,i) * pi * (1-p)L-i
其中 C(L,i) 是组合数,也就是 C(L,i) = L! / (i! * (L-i)!)
至此整个计算过程完成。不过有一个细节需要说明下,由于以下两个原因,你无法用计算机常规的浮点数计算来得到 P:
C(L,i) 值会很大,甚至会超过 float64 浮点类型的最大值(约为1e308),用浮点运算最终会变 Inf(无穷大)。
p 非常小,这会导致计算过程精度损失非常大,计算的累计误差无法忽略,答案和理论值大相径庭。
所以如果你真要去算这个概率,需要用无损计算的数学包。
甘泉:
我们这个概率的计算和RAID是类似的,我们可以将RAID的概念延伸一下,其实RAID的本 质就是replication,通过多个副本来解决一个或者多个节点出问题造成的影响。所以不管是本机的副本,还是跨网络或者跨地域的副本,本质上都是在用replication做冗余。
王旭:
其实只是丢数据的概率问题,没有云存储服务可以承诺绝不丢的。国内有个问题,就是——如果我在服务条款里说了可能丢数据,那用户就不愿意用了(“云还丢数据?!”),竞争对手的公关也要找上门来了;可是丢数据的 case 总是可能存在的,那么,一旦丢了就是没有尽到告知义务,而且还是会被炒作(“云还丢数据” again),但实际上这是个 SLA 嘛,生意上的事,最后要用钱解决的嘛。
AWS的两个主要资源层存储服务是 EBS 和 S3,后者是对象存储服务,强调数据可靠性,承诺11个9的 Durability,据说实际能达到更高;而EBS承诺的是年平均故障率(AFR),一年中发生块设备故障而导致卷无法使用的概率,这个要求实际上不高的。
对于存储服务,对象存储可以视为在线数据的最终存储方案,要保证更高的可靠性,而EBS是半永久存储方案,只有结合备份方案、多副本方案或者快照,才可以保证高可靠性的,教育用户是云服务行业不能回避的任务,骗用户说我绝对可靠是不行的,出来混是要还的。