同样是翻译官方文档,ring作为swift核心的模块,对于了解swift很有帮助。
注:英语水平有限,翻译的不好,有待改进。
The Rings
rings 决定数据在集群中应该存储的位置。有单独的环来管理账户数据库,容器数据库,和单独的对象,但是每一个ring工作在同一个方式下。这些rings被外部所管理,在那些服务器进程中,它们自己不修改rings,
它们通过其他的工具来给予新的修改到rings上。
ring使用一个来自路径的MD5哈希值的可配置的比特数作为虚节点索引来指定一个设备,这个从比特数值来的哈希值称之为虚节点的幂,2的虚节点的幂次方表明了虚节点的数量。划分全MD5哈希允许集群的其他部分工作一次分批进行,可以更有效率的完成,或者至少比在每一个项目分开或者整个集群一次运行复杂度要少。
其他可以配置的值是副本的数量,表明有多少虚节点->设备分配组成单独ring。对于给定的虚节点的编号,每个副本的设备将不会像其他副本的设备一样在同一个域。域可以通过,物理位置,电力分配,网络分配,或者其他可以减少多个副本在同一时刻不可用的特性来整合设备。
Ring Builder 环构造器
rings是被建立和管理是手动的通过一个ring-builder。ring-builder把虚节点分配到设备,然后生成一个最优化的Python结构来打包,序列化文件在磁盘上运送到服务器上。服务器进程只是不定期的检查文件的修改时间,需要的时候从新加载到ring 结构在内存的拷贝。由于ring-builder管理环变化的方法,使用一个稍旧的ring通常就是意味着三个副本中的一个小子集的虚节点将会错误的,可以很容易的解决。
ring-builder也保持它独有的有着ring信息和附加的需要建立新的rings的数据。保持构造器文件的多个备份是非常的重要。一种做法是,当拷贝ring文件的时候,拷贝构造器文件到每一个服务器上。另一种做法上传构造器文件到集群。构造器文件完全性的损失意味着创建一个新ring,几乎所有的虚节点将最终分配到不同的设备,因此几乎所有被存储的数据将会不得不被复制到新的位置。所以,从损失的构造器文件恢复是可能的,但是数据将肯定会在一段时间内是不可用的。
Ring Data Structure 环的数据结构
ring数据结构包含三个顶级域组成:在集群中的列表,一个设备id的列表的列表,表示虚节点到设备的分配。一个整数表示一个MD5哈希值转换的比特值,用哈希值来计算虚节点。
List of Devices
设备列表在Ring类的内部被称做devs。在设备列表里德每一天项目都是带有以下键值的字典:
id integer 列表中设备的索引
zone integer 设备所分配的区域
weight float 相比于其他设备,设备的相对权重。通常直接对应磁盘空间的总量与其他设备的比。例如一个1TB大小的设备或许有100.0权重,其他2TB大小的设备或许有200.0权重。权重也可以用作恢复平衡一个最终超出或者少于需求的数据。一个好的平衡权重是100.0允许灵活的降低权重如何需要的话。
ip string 服务器包含设备的ip地址
port int 服务器进程使用的TCP监听端口,用来服务请求的设备。
device string 服务器上设备的磁盘的名称。例如:sdb1
meta string 一个通用域用来存储设备的额外的信息。这个信息不是直接被服务器进程使用。但是在调试的时候非常有用。例如,安装时的时间和数据和硬件厂商可以被存储在这里。
注意:这个设备列表可能包含holes,或者设置为None的索引,表示已经从机器中移除的设备。通常情况,设备id是不会被重用的,有些设备当设置它们的weiget为0.0时会临时的失效,获得一个有效的设备列表(例如,正常的时间轮训)Python的代码如下: devices = [device for device in self.devs if device and device['weight']]
Partition Assignment list 虚节点分配列表
这个列表由设备id的array(‘I’)组成。最外的列表包含了每一个副本的array(‘I’)。每一个array(‘I’)的长度等于ring虚节点数。在array(‘I’)中的每一个整数是上面设备的列表的索引。虚节点列表在Ring类内部被称为as _replica2part2dev_id。
因此,创建分配到虚节点的设备字典的列表,python代码: devices = [self.devs[part2dev_id[partition]] for part2dev_id in self._replica2part2dev_id]
array(‘I’)用来做上百万虚几点的内存保护。
Partition Shift Value 虚节点位移值
虚几点的位移值在Ring类内部称为_part_shift。这个值用来位移MD5哈希值来计算虚节点,哪个数据应该属于的哈希值。只有哈希值的前4个字节被用在进程中。例如计算路径为/account/container/object 的虚节点,Python代码: partition = unpack_from('>I', md5('/account/container/object').digest())[0] >> self._part_shift
Building the Ring 构造环
构造环的初始化,首先通过设备的weight计算理想情况下被分配到每个设备的虚节点数。例如如果虚节点的幂是20的ring,将会有1,048,576个虚节点,如果这有1,000台设备有相等的权重,它们每一个都会被分到1,048,576个虚节点,设备通过它们需求的虚节点的数目,进行排序,并且在整个初始化的时候保持这个顺序。
然后,ring 构造器分配每一个虚节点的每一个副本到设备上,限制大多数虚节点在同一个点的同时,尽可能的远离其他的副本。ring 构造器更愿意分配一个副本到一个还没有副本存在的的域;没有这样的域是可以用的。ring构造器将会尝试去在不同的服务器上找一个设备;如果失败了,他将会只是找一个没有副本的设备;最后,如果所有其他的方法都耗尽了,ring构造器将会分配副本到有最少已经分配副本的设备。
当基于一个较老的ring来构建一个新ring时, 每一个设备限制的虚节点数需要从新计算。然后被从新分配的虚节点集合起来,所有被移除的设备将它们被分配的虚节点取消分配然后加入到收集的列表。所有的虚节点副本(由于额外的新的设备)可以分散到耐久性更好的,将会被取消分配然后加入到收集列表中。所有比它们所限制的虚节点数多的设备将随机的取消虚节点分配,然后加入到收集列表,最后收集到的虚节点被重新分配到设备使用相似于上面初始化分配的方法。
每当一个虚节点有一个副本被重新分配,重新分配的时间会被记录,当收集虚节点来重新分配时会被带入到账户中 ,以便于没有虚节点没移动两次在一个可配置的时间段里。这个时间段在RingBuilder类的内部称为min_part_hours。这样的限制是忽略虚节点的副本在设备上已经删除,这样的删除一个设备只会发生在设备失败和没有机会去重新分配。
由于收集虚节点进行重新分配的随机性,上面的进程不会总是完美的重新平衡一个环。为帮助达到一个更见平衡的环,重新平衡进程重复执行知道接近完美(小于1%)或者当平衡的提升到不到最少1%(表明我么可能不能得到完美的平衡由于不平衡区域的杂乱或者有太多的虚节点刚刚被移动)。
History
ring 的代码经历了在到达现在这个已经稳定了一段时间的版本之前经历了许多次的迭代式的修改,如果有了新的想法产生,这个算法很可能会被改变或者甚至从根本上改变。这一部分将尝试去描述以前的尝试过的想法,尝试去解释为什么它们被废弃了。
"live ring "选项被考虑过,每一个服务器可以维护它们自己的环的拷贝,使用gossip协议传达它们改变的。这个方法由于太复杂而且在有效的时间内正确的编码是很容易出现错误的,所以被废弃了。一个bug可以很容易把坏数据通过gossip带到整个集群并且很难恢复。一个外部的管理环可以简化这个过程,允许当数据被送出服务器之前进行完整的校验,确保每一个服务器使用一个ring在相同的时间轴。这也意味着这些服务器不会花费很多的资源维护rings。
一对"ring server"选项被考虑使用过。一个是所有环的查询可以通过调用一个在单独服务器或者组服务器的服务,但是由于涉及到延迟就被废弃了。另一个更像当前的进程但是服务器可以提交改变的请求到ring server来构造一个新的环。然后送回到服务器上。由于项目时间限制和因为ring的改变在当前足够的罕见,手工的控足够了,所以被废除了。然而,缺乏快速自动的ring改变,意味着其他系统的组成部分不得不编码来处理设备不可用在几个小时内知道有人手动更新ring。
当前的rign进程虚节点的每一个副本独立的分配到设备。有个ring版本尝试使用三分之一的内存,一个虚节点的第一个副本直接分配,其他两个通过"walking"ring知道找到额外的设备在其他域来决定。这个被废弃因为失去了有多少副本对于一个已经给予的虚节点被移动在一次的控制。保持每一个副本独立允许只移动一个虚节点副本在给定的时窗(除了由于设备故障)。使用额外的内存被视作是换来,移动数据在集群中更少次。
另一个ring涉及尝试虚节点对于设备的分配不存储在一个大列表在内存,但是每个设备被分配一个哈希 或者集合。虚节点从这些数据的哈希和最近设备集合将决定副本被存在哪里。然而,获得每一个合理分配的数据,设备不得不由很多的集合和查找这些集合来找到副本的开始合计。最后,内存保存不是很好,和更好的处理能力被使用,随意这个想法被放弃了。
一个完整的没有虚节点的环也被尝试过,但是因为徐几点帮助系统的其他组成部分,特别是复制,复制可以再虚节点和其他副本的批处理中尝试和重试,而不是每个数据项独立尝试和重试。目录结构的哈希值可以计算和比较其他副本较少目录的遍历和网络的流量。
虚节点和独立分配虚节点副本也允许最佳平衡集群。其他最佳的策略倾向于给出+-10%变化在设备平衡,设备相等权重的+-15%设备权重的变化。当前的策略允许我们使用+-3%和+-8%的变化。
多种哈希算法被尝试过。SHA提供了更好的安全性,倒是ring不需要安全可靠的加盟而且SHA比较的慢。Murmur更快,但是MD5是内建的,并且哈希计算只是整个请求处理时间中很小的一部分。总之,一旦服务器不能维护rings而且只能做哈希查找,MD5被选择因为它的通用性,好的分布,和足够快的速度。