OpenStack Object Storage(Swift)是OpenStack开源云计算项目的子项目之一。Swift的目的是使用普通硬件来构建冗余的、可扩展的分布式对象存储集群,存储容量可达PB级。
Swift并不是文件系统或者实时的数据存储系统,它是对象存储,用于永久类型的静态数据的长期存储,这些数据可以检索、调整,必要时进行更新。最适合存储的数据类型的例子是虚拟机镜像、图片存储、邮件存储和存档备份。
Swift无需采用RAID(磁盘冗余阵列),也没有中心单元或主控结点。Swift通过在软件层面引入一致性哈希技术和数据冗余性,牺牲一定程度的数据一致性来达到高可用性(High Availability,简称HA)和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。
Swift的主要特性如下:
Swift和Hadoop分布式文件系统(HDFS)都有着相似的目的:实现冗余、快速、联网的存储,它们的技术差异如下:
在分布式对象存储中,一个关键问题是数据该如何存放。Swift是基于一致性哈希技术,通过计算可将对象均匀分布到虚拟空间的虚拟节点上,在增加或删除节点时可大大减少需移动的数据量;虚拟空间大小通常采用2的n次幂,便于进行高效的移位操作;然后通过独特的数据结构 Ring(环)再将虚拟节点映射到实际的物理存储设备上,完成寻址过程。
图1 一致性哈希环结构
衡量一致性哈希的4个指标:
Swift使用该算法的主要目的是在改变集群的node数量时(增加/删除服务器),能够尽可能少地改变已存在key和node的映射关系,以满足单调性。
考虑到哈希算法在node较少的情况下,改变node数会带来巨大的数据迁移。为了解决这种情况,一致性哈希引入了“虚拟节点”(vnode,也称为partition)的概念: “虚拟节点”是实际节点在环形空间的复制品,一个实际节点对应了若干个“虚拟节点”,“虚拟节点”在哈希空间中以哈希值排列。
总的来说,Swift中存在两种映射关系,对于一个文件,通过哈希算法(MD5)找到对应的虚节点(一对一的映射关系),虚节点再通过映射关系(ring文件中二维数组)找到对应的设备(多对多的映射关系),这样就完成了一个文件存储在设备上的映射。
图2 对象、虚结点、节点间的映射关系
在设置虚结点数的时候,需要对系统预期的规模做充分考虑,假如集群的规模不会超过6000个结点,那么可以将虚结点数设置为结点数的100倍。这样,变动任意一个结点的负载仅影响1%的数据项。此时有6百万个vnode数,使用2bytes来存储结点数(0~65535)。基本的内存占用是6*(10^6)*2bytes=12Mb,对于服务器来说完全可以承受。
假设有65536(2^16)个node,有128(2^7)倍的partition数(2^23,则PARTITION_POWER=23)。由于MD5码是32位的,使用PARTITION_SHIFT(等于32- PARTITION_POWER)将数据项的MD5哈希值映射到partition的2^23的空间中。
按照Eric Brewer的CAP(Consistency,Availability,PartitionTolerance)理论,无法同时满足3个方面,Swift放弃严格一致性(满足ACID事务级别),而采用最终一致性模型(Eventual Consistency),来达到高可用性和无限水平扩展能力。
为了实现这一目标,Swift采用Quorum仲裁协议(Quorum有法定投票人数的含义):
Swift针对的是读写都比较频繁的场景,所以采用了比较折中的策略,即写操作需要满足至少一半以上成功W>N/2,再保证读操作与写操作的副本集合至少产生一个交集,即R+W>N。
在分布式系统中,数据的单点是不允许存在的。线上正常存在的replica数量是1的话将非常危险的,因为一旦这个replica再次错误,就可能发生数据的永久性错误。假如我们把N设置成为2,那么,只要有一个存储节点发生损坏,就会有单点的存在。所以N必须大于2。但N越高,系统的维护和整体成本就越高。所以,工业界通常把N设置为3。
Swift默认配置是N=3,W=2>N/2,R=1或2,即每个对象会存在3个副本,这些副本会被尽量存储在不同区域的节点上;W=2表示至少需要更新2个副本才算写成功。
当R=1时,意味着某一个读操作成功便立刻返回,此种情况下可能会读取到旧版本(弱一致性模型)。
当R=2时,需要通过在读操作请求头中增加x-newest=true参数来同时读取2个副本的元数据信息,然后比较时间戳来确定哪个是最新版本(强一致性模型)。
如果数据出现了不一致,后台服务进程会在一定时间窗口内通过检测和复制协议来完成数据同步,从而保证达到最终一致性。
图3 Quorum协议示例
Ring是Swift中最重要的组件,用于记录存储对象与物理位置间的映射关系。在涉及查询Account、Container、Object信息时就需要查询集群的Ring信息。
环是为了将虚拟节点(partition,分区)均衡地映射到一组物理存储设备上,并提供一定的冗余度而设计的,其数据结构由以下信息组成:
存储设备列表、设备信息包括唯一标识号(id)、区域号(zone)、权重(weight)、IP 地址(ip)、端口(port)、设备名称(device)、元数据(meta)。
Swift为账户、容器和对象分别定义了的Ring,其查找过程是相同的。Ring中每个partition在集群中都默认有3个replica。每个partition的位置由ring来维护,并存储在映射中。
Ring使用zone来保证数据的物理隔离。每个partition的replica都确保放在了不同的zone中。Zone只是个抽象概念,它可以是一个磁盘(disk drive),一个服务器(server),一个机架(cabinet),一个交换机(switch),甚至是一个数据中心(datacenter),以提供最高级别的冗余性,建议至少部署5个zone。
权重参数是个相对值,可以来根据磁盘的大小来调节,权重越大表示可分配的空间越多,可部署更多的分区。
当集群中发生存储节点宕机、新增(删)存储节点、新增(删)zone等必须改变partition和node间的映射关系时,还可以对Ring文件通过重新平衡(rebalance)来进行更新。当虚节点需要移动时,环会确保一次移动最少数量的虚节点数,并且一次只移动一个虚节点的一个副本。
总的来说,Ring引入一致性哈希的原因是为了减少由于增加结点导致数据项移动的数量来提高单调性;引入partition的原因是为了减少由于节点数过少导致移动过多的数据项;引入replica的原因是防止数据单点、提高冗余性;引入zone的原因是为了保证分区容忍性;引入weight的原因是为了保证partition分配的均衡。
Swift采用层次数据模型,共设三层逻辑结构:Account/Container/Object(账户/容器/对象)。每层节点数均没有限制,可以任意扩展。这里的账户和个人账户不是一个概念,可理解为租户,用来做顶层的隔离机制,可以被多个个人账户所共同使用;容器类似文件夹,代表封装一组对象;对象由元数据和数据两部分组成。
Swift采用完全对称、面向资源的分布式系统架构设计,所有组件都可扩展,避免因单点失效而扩散并影响整个系统运转;通信方式采用非阻塞式 I/O 模式,提高了系统吞吐和响应能力。
Swift组件包括:
图4 Swift系统架构
Swift支持的所有操作可以总结为下表:
表1 SwiftRESTful API总结
资源类型 |
URL |
GET |
PUT |
POST |
DELETE |
HEAD |
账户 |
/account/ |
获取容器列表 |
- |
- |
- |
获取账户元数据 |
容器 |
/account/container |
获取对象列表 |
创建容器 |
更新容器元数据 |
删除容器 |
获取容器元数据 |
对象 |
/account/container/object |
获取对象内容和元数据 |
创建、更新或拷贝对象 |
更新对象元数据 |
删除对象 |
获取对象元数据 |
Ring 的数据结构由三个顶层域构成,其中:
使用python读取/etc/swift/object.ring.gz存放的数据,可以获得以devs、 part_shift、 replica2part2dev_id 为key的dict类数据。
在Storage Node上运行着Linux系统并使用了XFS文件系统,逻辑上使用一致性哈希算法将固定总数的partition映射到每个Storage Node上,每个data也使用同样的哈希算法映射到partition上。
存储内容一般放在/srv/node/sdb1之类的路径下,其目录结构如下所示:accounts、async_pending、containers、objects、quarantined和tmp。其中accounts、containers、objects分别是账号、容器、对象的存储目录,async_pending是异步待更新目录,quarantined是隔离目录,tmp是临时目录。
图5 隔离对象的处理流程
Swift牺牲一定程度的数据一致性,来达到高可用性和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。
有理由相信,因为其完全的开放性、广泛的用户群和社区贡献者,Swift可能会成为云存储的开放标准,从而打破Amazon S3在市场上的垄断地位,推动云计算在朝着更加开放和可互操作的方向前进。
1) 《Openstack Swift 原理、架构与 API 介绍》,http://www.kankanews.com/ICkengine/archives/66411.shtml
2) 《深入云存储系统Swift核心组件:Ring实现原理剖析》,http://www.cnblogs.com/yuxc/archive/2012/06/22/2558312.html
3) 《深入云存储系统Swift核心组件:Ring数据结构及构建、重平衡操作》,http://www.cnblogs.com/yuxc/archive/2012/06/28/2568584.html
4) 《深入云存储系统Swift存储节点:存储实现分析》,http://www.cnblogs.com/yuxc/archive/2012/07/04/2575536.html
5) 《OpenStack对象存储——Swift开源云计算》,http://dev.yesky.com/244/33228744.shtml
6) 《讨论:HDFS和OpenStack对象存储的技术差异》,http://os.51cto.com/art/201202/314254.htm