Cluster map由device和bucket组成,它们都有id和权重���。Bucket可以包含任意数量item。item可以都是的devices或者都是buckets。管理员控制存储设备的权重。权重和存储设备的容量有关。Bucket的权重被定义为它所包含所有item的权重之和。CRUSH基于4种不同的bucket type,每种有不同的选择算法。
3.1 分层集群映射(cluster map)
集群映射由设备和桶(buckets)组成,设备和桶都有数值的描述和权重值。桶可以包含任意多的设备或者其他的桶,使他们形成内部节点的存储层次结构,设备总是在叶节点。存储设备的权重由管理员设置以控制相设备负责存储的相对数据量。尽管大型系统的设备含不同的容量大小和性能特点,随机数据分布算法可以根据设备的利用率和负载来分布数据。
这样设备的平均负载与存储的数据量成正比。这导致一维位置指标、权重、应来源于设备的能力。桶的权重是它所包含的元素的权重的总和。
桶可由任意可用存储的层次结构组成。例如,可以创建这样一个集群映射,用名为“shelf”的桶代表最低层的一个主机来包含主机上的磁盘设备,然后用名为“cabinet”的桶来包含安装在同一个机架上的主机。在一个大的系统中,代表机架的“cabinet”桶可能还会包含在“row”桶或者“room”桶里。数据被通过一个伪随机类hash函数递归地分配到层级分明的桶元素中。传统的散列分布技术,一旦存储目标数量有变,就会导致大量的数据迁移;而CRUSH算法是基于桶四个不同的类型,每一个都有不同的选择算法,以解决添加或删除设备造成的数据移动和整体的计算复杂度。
3.2 副本放置(Replica Placement)
CRUSH 算法的设置目的是使数据能够根据设备的存储能力和宽带资源加权平均地分布,并保持一个相对的概率平衡。副本放置在具有层次结构的存储设备中,这对数据安全也有重要影响。通过反射系统的物理安装组织,CRUSH算法可以将系统模块化,从而定位潜在的设备故障。这些潜在故障的资源包括物理的,比如共用电源,共用的网络。通过向集群映射编码信息,CRUSH副本放置策略可以将数据对象独立在不同故障域,同时仍然保持所需的分布。例如,为了定位可能存在的并发故障,应该确保设备上的数据副本放置在不同的机架、主机、电源、控制器、或其他的物理位置。
CRUSH算法为了适应千篇一律的脚本,像数据复制策略和底层的硬件配置,CRUSH对于每份数据的复制策略或者分布式策略的部署方式,它允许存储系统或 者管理员精确地指定对象副本如何放置。例如,有的会选择两个镜像来存储一对数据对象,有的会选择3个镜像来存储2个不同的数据对象,还有的会选择6个甚至更多的便宜廉价RAID-4硬盘设备来存储等等。
函数入口:
/**
* crush_do_rule - calculate a mapping with the given input and rule
* @map: the crush_map
* @ruleno: the rule id
* @x: hash input
* @result: pointer to result vector
* @result_max: maximum result size
* @weight: weight vector (for map leaves)
* @weight_max: size of weight vector
* @scratch: scratch vector for private use; must be >= 3 * result_max
*/
int crush_do_rule(const struct crush_map *map,
int ruleno, int x, int *result, int result_max,
const __u32 *weight, int weight_max,
int *scratch) //对照此函数与算法伪代码基本可以看出crush在做什么事情。部分数值计算我也看不懂为什么他这么做,水平有限。
CRUSH_RULE_TAKE /* arg1 = value to start with */
CRUSH_RULE_CHOOSE_FIRSTN = 2, /* arg1 = num items to pick */ crush_choose_firstn()
/* arg2 = type */
CRUSH_RULE_CHOOSE_INDEP = 3, /* same */ crush_choose_indep()
CRUSH_RULE_EMIT = 4, /* no args */ return results
在算法1的伪代码中,每个规则都包含了一系列应用在一个简单运行环境的操作。CRUSH函数的整型输入参数就是一个典型的对象名或者标示符,这个参数就像一堆可以被复制在相同机器上的对象复制品。操作take(a)选择了一个在存储层次的bucket并把这个bucket分配给向量i,这是为后面的操作做准备。操作select(n,t)迭代每个元素i,并且在这个点中的子树中选择了n个t类型的项。存储设备有一个绑定类型,并且每个bucket在系统中拥有一个用于分辨buckets中classes的类型区域(例如哪些代表rows,哪些代表cabinets等)。对于每个i,select(n,t)都会从1到n迭代调用,同时通过任何中间buckets降序递归,它伪随机地选择一个通过函数c(r,x)嵌套的项,直到它找到请求t中的一个项。去重后的结果项n|i|会返回给输入变量i,同时也会作为随后被调用的select(n,t)操作的输入参数,或者被移动到用于触发操作的结果向量中。
tack(a) :选择一个item,一般是bucket,并返回bucket所包含的所有item。这些item是后续操作的参数,这些item组成向量i。