ceph数据分布

ceph的存储是无主结构,数据分布依赖client来计算,有两个条主要路径。

1、数据到PG

2、PG 到OSD

有两个假设: 第一,pg的数量稳定,可以认为保持不变; 第二, OSD的数量可以增减,OSD的存储空间权重不等;

由于 PG的数量保持不变,由数据来找PGID的环节可以简单处理,对数据的key来取hash值再对pg的总数取模即可唯一确认pgid,pgid=hash(data_key)/pg_num。

难点在于从PG到OSD,如果直接用 hash(pgid)/osd_num的模式,则OSD有增减的时候数据就有无规律的迁移,并且也无法体现OSD的不同权重。

Crush算法就是来解决这个问题的,Crush目的是随机跳出一个OSD,并且要满足权重越大的OSD,挑中的概率越大。

每个OSD有不同的容量,比如是4T还是12T的容量,可以根据每个OSD的容量定义它的权重,以T为单位, 比如4T权重设为4,12T则设为12。

如何将PG映射到不同权重的OSD上面?这里可以直接采用CRUSH里面的Straw抽签算法。

核心步骤:

1)计算HASH

draw = CRUSH_HASH( PG_ID, OSD_ID, r ),其中把r当做一个常数,将PG_ID, OSD_ID一起作为输入,得到一个HASH值。

2)增加OSD权重

osd_straw =( draw &0xffff ) * osd_weight

draw &0xffff 得到一个0-65535的数字,再与OSD的权重相乘,以这个作为每个OSD的签长, 权重越大的,数值越大。

3)遍历选取最高的权重

high_draw

Crush所计算出的随机数,是通过HASH得出来,可以保障相同的输入会得出同样的输出结果。

这里只是计算得出了一个OSD,在Ceph集群中是会存在多个副本,如何解决一个PG映射到多个OSD的问题?

将常量r加1, 再去计算一遍,如果和之前的OSD编号不一样, 那么就选取它;如果一样的话,那么再把r+2,再重新计算,直到选出三个不一样的OSD编号。

如果样本容量足够大, 随机数对选中的结果影响逐渐变小, 起决定性的是OSD的权重,OSD的权重越大, 被挑选的概率也就越大。

样本容量足够大,到底是多大? 到底多大才能按照尽可能按照权重来分布,当然是尽量小的样本才好。

样本容量主要由PG和OSD的数量多少来决定,其中最关键的还是OSD数量,如果OSD很少(比如5块盘)也能尽量按照权重分布才好。

PG的数量主要是根据数据预估和OSD的数量来定,有个理论参考数,PG数量 =(OSD数量* 100)/副本数,但是PG数量少影响后面的扩容,太多又占用过多资源,需要有一个平衡。

基于上述考虑,写了一个很简单的程序来验证下数据分布平衡性。

假定OSD数量为5并且权重随机,PG的数量为5000。

结果1:

1.随机生成5个OSDID和对应权重

OSDID=I0N@6nt5pOhjY$g;权重=32.0

OSDID=.nIjl%3zs3aoE7K;权重=16.0

OSDID=S5O9bSS4NMo%qDN;权重=1.0

OSDID=t$lZF91ofuvOKcn;权重=24.0

OSDID=!E2Ia8XE^Jzb5Dz;权重=12.0

2.在pg数量为5000的时候,PG的分布结果:

OSDID=!E2Ia8XE^Jzb5Dz;权重=12.0;拥有的PG数量=625

OSDID=I0N@6nt5pOhjY$g;权重=32.0;拥有的PG数量=2682

OSDID=t$lZF91ofuvOKcn;权重=24.0;拥有的PG数量=1554

OSDID=.nIjl%3zs3aoE7K;权重=16.0;拥有的PG数量=139

结果2:

1.随机生成5个OSDID和对应权重

OSDID=C%EN$UM!e8nZy.R;权重=1.0

OSDID=1iTDBnZeeQ6^Uos;权重=32.0

OSDID=%EMc6a4V5cWi%7D;权重=2.0

OSDID=M7WKDUjLrQaV42D;权重=64.0

OSDID=7OVTO@l$XLE$OV$;权重=8.0

2.在pg数量为5000的时候,PG的分布结果:

OSDID=1iTDBnZeeQ6^Uos;权重=32.0;拥有的PG数量=1201

OSDID=7OVTO@l$XLE$OV$;权重=8.0;拥有的PG数量=18

OSDID=M7WKDUjLrQaV42D;权重=64.0;拥有的PG数量=3781

结果3:

1.随机生成5个OSDID和对应权重

OSDID=TSvabIIG#9IssWW;权重=12.0

OSDID=XglajmN2q3f5qRI;权重=0.8

OSDID=ZEeeX^Wp9tHaxuA;权重=0.5

OSDID=PSiiRAwddyc^ThW;权重=32.0

OSDID=nPI^YbDr0ttVzGa;权重=8.0

2.在pg数量为5000的时候,PG的分布结果:

OSDID=nPI^YbDr0ttVzGa;权重=8.0;拥有的PG数量=319

OSDID=PSiiRAwddyc^ThW;权重=32.0;拥有的PG数量=3816

OSDID=TSvabIIG#9IssWW;权重=12.0;拥有的PG数量=865

package com.test.zhangzk.crush;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;


public class TestCephCrush {
	static String str = "abcdefghijklmnopqrstuvwxyzABCDEDFGHIJKLMNOPQRSTUVWXYZ0123456789.@!#$%^&*";
	static Float[] factories =new Float[] {0.25f,0.5F,0.8f,1f,2f,4f,8f,12f,16f,20f,24f,32f,64f};
	static int pgidCount = 5000;
	static int osdCount = 5;

	public static void main(String[] args) {
		List<String> pgidList = getRandomPgIdList(pgidCount);
		List<OSDBean> osdList = getRandomOSDIdList(osdCount);
		HashMap<String,Integer> keyCount = new HashMap<String,Integer>();
		
		for(int i=0;i<pgidCount;i++) {
			float maxStraw = 0.0f;
			float osdFactor = 0.0f;
			String osdId = "";
			for( int j=0;j<osdCount;j++) {
				String key = pgidList.get(i) + osdList.get(j);
				int hashCode = key.hashCode() & 0xffff;
				float straw = hashCode * osdList.get(j).getFactor();
				if( maxStraw < straw) {
					maxStraw = straw;
					osdFactor = osdList.get(j).getFactor();
					osdId = osdList.get(j).getId();
				}
			}
		
			String key =  "OSDID="+osdId  + ";权重=" + osdFactor;
			Integer v = keyCount.get(key);
			if( v == null ) {
				keyCount.put(key, 1);
			}else {
				keyCount.put(key, v+1);
			}	
		}
		

		System.out.println("2.在pg数量为" + pgidCount +"的时候,PG的分布结果:");
		for(String k:keyCount.keySet()){
			System.out.println(k + ";拥有的PG数量=" +keyCount.get(k));
		}
	}
	
	private static List<String> getRandomPgIdList(int pgidCount){
		// TODO Auto-generated method stub
		List<String> pgidList = new ArrayList<String>();
		java.util.Random r = new Random(System.currentTimeMillis());
		for( int i=0;i<pgidCount;i++) {
			StringBuilder sb = new StringBuilder();
			for( int j=0;j<10;j++) {
				sb.append(str.charAt(r.nextInt(str.length()-1)));
			}
			pgidList.add(sb.toString());
		
		}
		return pgidList;
	}
	
	private static List<OSDBean> getRandomOSDIdList(int osdCount){
		System.out.println("1.随机生成"+ osdCount + "个OSDID和对应权重");
		// TODO Auto-generated method stub
		List<OSDBean> osdList = new ArrayList<OSDBean>();
		java.util.Random r = new Random(System.currentTimeMillis());
		for( int i=0;i<osdCount;i++) {
			StringBuilder sb = new StringBuilder();
			for( int j=0;j<15;j++) {
				sb.append(str.charAt(r.nextInt(str.length()-1)));
			}
			OSDBean osd = new OSDBean();
			osd.setId(sb.toString());
			osd.setFactor(factories[r.nextInt(factories.length)]);
			System.out.println( "OSDID=" + sb.toString()+ ";权重="+ osd.getFactor() );
			osdList.add(osd);
		
		}
		return osdList;
	}
}

class OSDBean {
	private String id;
	private float factor;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public float getFactor() {
		return factor;
	}

	public void setFactor(float factor) {
		this.factor = factor;
	}
}

猜你喜欢

转载自blog.csdn.net/zhangzhaokun/article/details/132315890