43°

ceph-pg哈希

序言

ceph简介

Ceph 是一个统一的分布式存储系统,设计初衷是提供较好的性能、可靠性和可扩展性。底层的rados分布式存储是ceph的精华所在,ceph以rados为基础在上层封装了对象、块、文件存储接口。客户端可以通过ceph提供的librados库直连rados集群,待存对象通过计算获取指定机器,然后将数据发送到该机器上存储。因为都是通过计算的方式,所以rados不需要维护一个中心table来存储对象所在的位置,这种计算型的方式不仅快速,而且能够节省节点的资源。 下图为ceph的总体框架图:

 

 

数据如何映射

Ceph对集群中所有存储资源进行池化管理,对象通过计算直接映射到底层的osd中,为了更好的管理和映射数据,ceph有如下的概念。 - object: 用户需要存储的对象,可以是文档、视频、音频等等文件,用户在存储的时候需要指定唯一的对象名。 - pool:资源池,这是一个虚拟概念,一个集群可以分出多个pool,也可以使用单个pool,但是必须有一个pool。 用户可以针对不同的pool指定不同的CRUSH规则,也可以针对不同的pool指定不同的数据冗余策略。 - pg:全称 placement groups, 一个pool对应多个pg, 通过hash对象会存储到pool中的特定pg中。在创建pool的时候就要把pg数量规划好。pg数量只可以增大不可以缩小。 - osd:最终的数据都是存储在特定的osd上,一般情况下一个osd会管理一块磁盘。

对象的映射逻辑架构如下:

pg哈希算法

pg哈希算法代码:

/* 
x: 对象key值
b: pg 数
bmask: 掩码
*/
static inline int ceph_stable_mod(int x, int b, int bmask)
{
    if ((x & bmask) < b)
        return x & bmask;
    else
        return x & (bmask >> 1);
}

object通过hash对应到不同的pg中,ceph使用掩码的方式来代替取模操作。例如PG数目对应的最高比特位为n,则其掩码为2^n -1, 将某个对象映射到PG时,直接使用 object & (2^n -1)即可。这种直接与的方式其实就是取object二进制的后 n 位作为pg number,这种方式不仅高效,而且运算速度快。 可能有人会问这个n是怎么来的,其实n就是PG数二进制表示中1最高位的1。ceph中如果设置PG数目为a,那么程序会得到一个最小的n,使 a <= 2^n,例如:(a=12, n=4), (a=16, n=4), (a=17, n=5),以此类推。 但是这种方案存在一个潜在的问题,如果运维人员设置的PG数是2的幂次方的话,那么这种方案比较完美。如果PG数目不是2的幂次方的话这种映射会产生空穴,即将某些对象映射到一些实际上不存在的PG上。如下图所示:

 

上图中如果如果PG数目为14,但是n=4,直接hash的话会产生 0~15 共计16中不同的结果,但是12 ~ 15这4个PG是不存在的,为了解决这个问题ceph做了降级方案。因为 n 为PG数的最高位,因此可以得到如下的方程式。

2^(n-1) < PG数 <= 2^n

由此可得 [0,2^(n-1)]内PG一定是存在的,可以通过 object & 2^(n-1) 来将那些实际上不存在的PG重新映射到这个小的区间内,如下图所示:

如上图所示,ceph会将映射出不存在的PG重新映射到前面存在的区间中。所以如果ceph集群设置的PG数目不是2的幂次方的话,可能会造成中间某一段的PG所承载的数据高于其它PG,例如:PG数为12时会造成4,5,6,7四个PG所承载的对象数是其它PG的两倍,所以集群在稳定时PG数最好的2的幂次方。

PG扩容

在现实使用中如果需要进行扩容,在pg双倍扩容的情况下可以一步步的扩容以减少一步到位的扩容带来的数据迁移影响。 例如: 将 pg 数从 8 扩容到 16 ,如果直接扩容到16会迁移 1/2 的数据,这种情况下集群内部数据迁移可能会影响用户的使用,管理员可以采取 8 -> 12 -> 16这样的扩容方式。 根据上述图的描述,从8 -> 12 会迁移集群内部 1/4 的数据,也就是将pg数为 0~3 的数据迁移到 8~11 中,而 4~7 的数据是不会迁移的(因为重新hash它还是落到 4~7 这个区间),数据迁移完成后可以再将 pg 数从 12 ~16,这种情况下还是会迁移 1/4 的数据(将pg为 4~7 的数据迁移到 12~15 中),这样分两步迁移数据总的数据迁移量是一样的,都是 1/2,但是分步迁移的话可以缓解一段时间内的数据迁移量。

如果不是进行双倍扩容的话情况可能就比较复杂了,也可以采取这种通过增长迁移时间来换取短时间内少迁移数据的方案。

直接取模的弊端

可能有人会问为什么不使用简单的取模呢,因为这种方式在扩容的时候迁移的数据量无法控制。

设: pg 数为 a,需要将 pg 数扩容至 b, a 和 b 的最小公倍数为 d,某个对象的key值为 q

由此可以得到如下两个方程式: ca + e = q, e < a, q < d; ub + v = q, v < b, q < d;

因为最小公倍数为 d, 如果 c != 0 && u != 0, 所以 ca != ub, 所以 e != v

当 c == u == 0的情况下, e == v, 所以需要迁移数据比例为: (d-a)/d

由此可以得到直接取模的方式无法控制迁移的数据量,例如 pg 数从 8 -> 12,直接取模的话需要迁移 (24 - 8) / 24 = 2/3 的数据。

作者:冯杰

本文由【滴滴云】发布于开源中国,原文链接:https://my.oschina.net/u/4244677/blog/3160080

全部评论: 0

    我有话说: