hbase的预分区,以及rowkey的设计

未了防止habse的热点问题(单节点访问量太大或者插入到单台服务器节点),以及为了避免region的split的时候耗费时间,
可以提前进行预分区,但是预分区后rowkey是怎么落入到对应的分区呢?
这个问题到后面再解释;

private static Configuration conf = new Configuration();
public static void main(String[] args) throws IOException {
	String arg[] = new GenericOptionsParser(args).getRemainingArgs();
	if(arg.length<3) {
		System.out.println("参数不够!");
	}
	for(String a : arg) {
		System.out.println(a);
	}
	String tableName = "lzpTest";
	List<String> columnFamily = new ArrayList<>();
	conf.set("hbase.zookeeper.quorum","archive.cloudera.com,secondarynamenode2,datanode9");
	columnFamily.add("cf1");
	System.out.println("开始创建。。。。");
	createTableBySplitKeys(tableName,columnFamily);
}

进行创建预分区表,这里有两种方法来创建预分区,第一:getSplitKeysWithReginNum(int reginNum)方法reginNum是分区数,第二种:getSplitKeys()方法

    public static boolean createTableBySplitKeys(String tableName,List<String> columnFamily) {
	if(StringUtils.isBlank(tableName) || columnFamily ==null|| columnFamily.size()<0) {
		System.out.println("表名不存在!");
		return false;
	}
	try {
		System.out.println("正在创建。。。。");
		HBaseAdmin admin = new HBaseAdmin(conf);
		if(admin.tableExists(tableName)) {
			admin.disableTable(tableName.getBytes());
			admin.deleteTable(tableName.getBytes());
		}
		HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
		for(String family: columnFamily) {
			tableDescriptor.addFamily(new HColumnDescriptor(family));
		}
		byte[][] splitKeys = getSplitKeys();
		//创建表,指定splitkeys
		admin.createTable(tableDescriptor, splitKeys);
		System.out.println("预分区表创建成功!");
		
	} catch (IOException e) {
		e.printStackTrace();
		return false;
	}
	return true;
}

分区方法1,可以自己修改分区的规则,在keys数组,填写自己想要分区的规则;注意:为什么后面会跟着一个"|",是因为在ASCII码中,"|"的值是124,大于所有的数字和字母等符号,当然也可以用“~”(ASCII-126)。分隔文件的第一行为第一个region的stopkey,每行依次类推,最后一行不仅是倒数第二个region的stopkey,同时也是最后一个region的startkey。也就是说分区文件中填的都是key取值范围的分隔点,如下图所示:

    /**
 * 创建10个分区
 * @return
 */
private static byte[][] getSplitKeys() {
	String[] keys = new String[]{"10|","20|","30|","40|","50|","60|","70|","80|","90|"};
	byte[][] splitKeys = new byte[keys.length][];
	//必须使用treeset进行排序,否则在创建表时,会报错
	TreeSet<byte[]> rows = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);//升序排列
	for(int i =0;i<keys.length;i++) {
		rows.add(Bytes.toBytes(keys[i]));
	}
	
	Iterator<byte[]> rowKeyIter = rows.iterator();
	int i =0;
	while(rowKeyIter.hasNext()) {
		byte[] tempRow = rowKeyIter.next();
		rowKeyIter.remove();
		splitKeys[i] = tempRow;
		i++;
	}
	return splitKeys ;
}

分区方法2,可以根据需要,动态创建分区数

    /**
 * 动态创建分区数
 * @param reginNum
 * @return
 */
private static byte[][] getSplitKeysWithReginNum(int reginNum) {
	int total = 1000;//总步长
	int num = reginNum;//分区数
	
	//步长
	int step = total/num;
	//多余的步长
	int tail = total%num;
	//标准步长和非标准步长分界点
	//为了减少最后一个分区与其他分区的区间差别
	int split_step = num-tail-1;
	//总位数
	int length = Integer.toString(total-step).length();
	//标准化位数
	String format = "%0"+length+"d";
	//第一个分界点
	int limit = step;
	//准备预分区数组
	byte[][] splitKeys = new byte[num-1][];
	//遍历
	for(int i =0;i<num-1;i++) {
		if(i<split_step) {
			if(Integer.toString(limit).length()<length) {
				splitKeys[i] = String.format(format, limit).getBytes();
			}else {
				splitKeys[i] = Integer.toString(limit).getBytes();
			}
			limit += step;
		}else {
			if(Integer.toString(limit).length()<length) {
				splitKeys[i] = String.format(format, limit).getBytes();
			}else {
				splitKeys[i] = Integer.toString(limit).getBytes();
			}
			limit += (step+1);
		}
	}
	return splitKeys;
}
posted @ 2017-06-12 13:47  希洪小时  阅读(973)  评论(0编辑  收藏  举报