未了防止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;
}