Swift中所谓的Ring就是基于一致性Hash所构建的环。Ring包括以下三种重要的数据结构。
一 相关数据结构说明
1 设备表
设备表中的每一项都对应一个Device,其中记录了该Device的具体位置信息,包括Device ID,所在Region,Zone,IP地址以及端口号,以及用户为该Device定义的权重(Weight)等。Device容量大小不一时,可以通过Weight值保证Partition均匀分布,容量较大的Device拥有拥有更大的权重,也容纳更多的Partition。
比如,一个1T大小的Device有100的权重而一个2T大小的Device有200的权重。
2 设备查询表(Device Lookup Table)
存储Partition的各个副本(默认为3个)与具体Device的映射关系。
从列的维度分析:5,12,1可以这样理解,0号partition的3个副本分别存放在5号、12号、1号设备上。
从行的维度分析:5,0,1,4,12表示针对第1个副本,0,1,2,3,4这5个partition分别存放在5号,0号,1号,4号,12号设备上。
从表格的维度分析:每个表格的信息是设备表中的Device编号,根据这个编号,可以去设备表中检索到该Device的具体连接信息(Device ID、IP以及端口号等信息)。
3 Partition移位值(Partition Shift Value):表示在Hash之后将Object名字二进制移位的位数。
二 相关数据结构解释
为了减少由于增加、减少节点所带来的数据迁移,Swift在对象和存储节点的映射之间增加了Partition的概念,使得对象到存储节点的映射过程,变成了由对象到Partition再到存储节点的映射。
Partition的个数一旦确定,在整个运行过程中不会改变的,所以对象到Partition的映射是不会改变的。在增加或者减少节点的情况下,通过改变Partition到存储节点的映射来完成数据的迁移。
对象到Partition这层的映射是通过Hash函数以及二进制移位操作,即这里的Partition Shift Value完成的,Partition到存储节点的映射是通过设备查询表完成的。
三 代码解读
Swift接收到用户请求中包含了数据的路径名称,比如对于Object的请求,可能为“GET account_name/container_name/object_name”,Swift需要将这个路径映射到Partition上:
#swift/common/ring/ring.py
class Ring(object):
def get_part(self, account, container=None, obj=None):
"""
获取account/container/object对应的partition
"""
#首先计算Hash值
key = hash_path(account, container, obj, raw_digest=True)
if time() > self._rtime:
self._reload()
#然后位移得到partition
part = struct.unpack_from('>I', key)[0] >> self._part_shift
return part