ring代表存储在硬盘上的实体(entity)名称和实际物理位置的映射。accounts,containers,objects都有各自的ring。当swift的其它组件(比如replication)需要对object,container或者account操作时,需要使用不同的ring去确定各自在集群上的位置。
ring 使用zone,device,partition和replica来维护这些映射(mapping)信息。ring中每个partition在集群中都(默认)有3个replica (副本)。
每个partition的位置由ring来维护,并存储在映射(mapping)中。当proxy server转发的客户端请求失败时(多数情况下,失败源自存储或转移数据时目标server无响应),ring同样负责决定哪一个device将接手请求。
ring中的数据由zone保证各自隔离。每个partition的replica被放在不同的zone上。一个zone可以是一个硬盘(disk drive),一个server,一个机架(cabinet),一个交换机(switch),甚至是一个数据中心(datacenter)。
swift安装时,ring的device会均衡的划分每个partition。当partition需要移动时(例如新device被加入到集群),ring会确保一次移动最少数量的partition,并且一次只移动一个partition的一个replica。
简单的说:
ring是一个管理存储资源的数据结构。
ring可以根据需要进行配置,比如副本数(replication)、partiton数(虚结点数) 、partition最小移动间隔。
ring是独立于account/container/object server的
ring使用一致性hash管理account/container/object与真实device之间的映射
ring 维护副本(replica)
通过以下命令生成ring:
swift-ring-builder <builder_file> create <part_power> <replicas> <min_part_hours>
其中:
part_power: partition的幂次方。和真实的磁盘(disk drive)数目有关。实践中,partition的数目设置成磁盘数的100倍会有比较好的命中率。比如,预计机群不会使用超过5000块磁盘,那么,partition数即500 000,那么19( 2^19=524288)为需要设置的part_power数。
注:partition实际上是真实存储设备(device)的映射,多个partition会对应于一个存储设备(device)。存储设备的增减不会影响partition的总数。因此,可以保证基于设备的存储结点不会因为设备的增减而剧烈抖动。
replicas:副本的数目。主要是为了容灾。副本数默认为3,副本数目越多,实际上用来存储的partition就会越少。同一个partition的不同replica被放置在不同的zone上。
min_part_hours:最小移动间隔。partition会因为一些原因移动,实际是partition上的数据移动。为了避免网络拥塞,partition不会频繁的移动。默认最小移动间隔为一小时。
swift-ring-builder <builder_file> add z<zone>-<ip>:<port>/<device_name>_<meta> <weight>
zone: swift中用来隔离数据。我们应该根据不同的故障原因分割不同的zone,用以保证故障发生时不会影响整体数据安全。
device_name: swift的storage node配置时挂载的逻辑磁盘名。一个storage node可以有多个device,根据device_name区分。
weight:用来均衡 (balance)drive的 partition在集群中的分布。比如当一个集群上有不同大小的drive时,weight大的device会分到更多的partition。weight是相对比较而言的。比如1T的磁盘weight为100,那么2T的磁盘weight应为200。
举例:
swift-ring-builder object.builder create 18 3 1 swift-ring-builder object.builder add z1-192.168.1.32:6000/sdb1 1 swift-ring-builder object.builder rebalance
注: ring 详细内容会在《swift实战进阶》中从源代码层面进一步介绍(以下简称《进阶》)。
图1可以帮助理解swift中几个概念
图1
简单的说,ring 和proxy node 没有直接关系。
我们可以在proxynode上构建ring ,当然也可以在其他server上构建。关键在于,ring构建所生成的*.ring.gz文件,是所有node都需要的。
proxy node需要根据ring的信息处理各种客户端请求;storagenode需要根据ring 的信息管理分发数据。我们需要把这些文件分发到所有的node上。
object server 是个简单的大对象(blob)存储server,可以用来操作(检索和删除)本地device上的object。object以二进制文件的形式和元数据(metadata)存储在文件统(filesystem)上,元数据(metadata)放在文件系统的扩展属性(xattrs)中。这潜在的要求object server需要支持有扩展属性(xattrs)的文件系统(file system)。一些文件系统,像ext3,默认的xattrs属性是关闭着的。
container server的主要工作是列表(listing)object 。container server 并不知道object存在哪,只知道指定container里存的哪些object。 这些object信息以sqlite数据库文件的形式存储, 和object一样在集群上做类似的备份。containerserver也跟踪(trace)做一些统计,比如object的个数,container的使用情况。
account server列表container。
注:虽然container中对object数目没有限制,但出于性能考虑。adrian otto[1]不建议container的object数超过百万。
如图2所示:
swift的结构非常简单。客户端从auth那里拿到token,根据account/container/object通过proxy的ring查询后,转送请求(request)到指定的storage node。
swift中的object都是私有的(private)。swift 1.2之前,container可以通过设置CDN-enabled的标识使得object变为公开。swift 1.2之后,CDN标识被剥离(对于专注存储的swift而言,这无可厚非),object访问只能以私有方式被访问。
注意:图2是仿照Haystack的结构画的,目的是对照Haystack对swift扩展理解。实际操作中web server 和CDN都不是必须的!
图二
proxy负载方面:可以增加proxy node 的数目均衡负载。负载均衡可以使用硬件和软件均衡。文献[2]的负载均衡小节给出了2种思路:一是集群总控机根据负载情况全局调度,二是采用DHT(一致性hash)的方法。前者难度在于调度的粒度较难把握,由于网络拥塞、机群热点等原因会造成一些调度问题;后者难度在于需要对数据量和访问量进行预估,以确定hash结点数目。软件方面的负载均衡也可以参考一些开源的解决方案,诸如Pen 和 Pound。
proxy缓存方面:proxy 使用memcached作为缓存。memcached有自身存储的不足,比如在内存内进行操作、没有list相关操作(不同的业务情况会有不同的需求)。通过实现缓存持久化或半持久化可以部分提高缓存的命中率。
CDN方面:swift作为存储系统做了很大简化。可以参考文献[3]中Haystack的设计结构对swift进行扩展。基于CDN可以有效提高命中率、缓解后端存储设备的压力。
[1] adrian otto的博客:http://adrianotto.com/2010/09/openstack-os-is-great-for/
[2 ]杨传辉 opendoc《分布式系统工程实践》
[3] Finding a needle in Haystack: Facebook’sphoto storage