hbase预分区误区

预拆分表通常是一种比较好的实践。如果预先拆分表,则必须了解rowkey将如何分布在region边界上。是否所有的region都有rowkey能够访问。
本文介绍,以16进制字符串作为rowkey或rowkey前缀可能进入的误区以及解决方式。

错误示例

举一个为什么这一点很重要的示例,考虑使用可显示十六进制字符作为键的前导位置的示例(例如,“0000000000000000”到“ffffffffffffffff”)。

创建预分区表的代码:

String startKeyString="0000000000000000";
String endKeyString="ffffffffffffffff";
int numRegions=10;
admin.createTable(tableDescriptor,startKeyString.getBytes(),endKeyString.getBytes(),numRegions);

犯错的点:直接使用starKey、endKey转byte[],然后按numRegions划分region,导致非a到f,0-9之间的字符,为后来使用16进制表示rowkey或rowkey埋坑。

//上面创建表函数,与下面的方法等价,生成一样region分布的表
int splitTimes=numRegions-3;//因为有两个空数组作为rowkey的开始和结束rowkey
byte[][] splitKeys=Bytes.split(startKeyString.getBytes(),endKeyString.getBytes(),splitTimes);
admin.createTable(tableDescriptor,splitKeys)

执行代码后,table的region分布如下,注意第一个字符:

图片

当我们使用十六进制的字符作为rowkey前缀的时候,就会发生热点分区的问题。记录都会集中在如下四个region,因为十六进制字符rowkey的值区间为[0-9]和[a-f]。

图片

做一些验证,向表中插入数据

byte[] rowKey=Bytes.toBytes("a");
 System.out.println(new String(rowKey)+"_"+rowKey.length);
 Put put = new Put(rowKey);
 put.addColumn(Bytes.toBytes(familyName), Bytes.toBytes(columnName), rowKey);
 table.put(put);

发现如下region接收到一次请求,即"a"与各个region的start key首字母与end key比较,“a"会落在”_"与"f"之间,所以此条记录会落在次rowkey区间的region上。
图片

另外需要注意的一点,rowkey字符串的每个字节的ascii码会与startkey、endkey的每一个字节的ascii码比较。

/** 因为最后一个字符"e"的ascii码比"f"小,所以仍然落在split key "ffffffffffffffff"之前的region分区 */
byte[] rowKey=Bytes.toBytes("fffffffffffffffe");
/** 因为没有第三个字符与split key比较,所以"ff"小于"ffffffffffffffff" */
byte[] rowKey=Bytes.toBytes("ff");

上面两个rowkey的记录仍然会落在如下分区
图片

错误分区代码总结

  1. 预拆分表通常是一种最佳实践。但您需要让预拆分表满足rokwey可以访问任意的分区。虽然这个例子演示了十六进制键空间的问题,但任何键空间都可能发生同样的问题。需要了解你的数据,要知道rowkey的展示格式,知道各个region的start key与end key的展示格式。
  2. 错误的分区代码尽管不适合,但只要加以改进,将所有创建的region都可以在rowkey键空间中访问,那么使用十六进制键(更常见的目的是,可显示rowkey数据)仍然可以作为预拆分表的方案。

正确示例

下面是如何为十六进制格式的rowkey进行预分区的正确示例:

正确操作方式:通过将字符串转为16进制的BigInteger后,在按照numRegion进行分组,然后再将BigInteger转化为16进制表示的字符串,最终转化为byte[]。

public static byte[][] getHexSplits(String startKey, String endKey, int numRegions) {
    byte[][] splits = new byte[numRegions-1][];
    //将16进制表示的字符串startKey转换为16进制的BigInteger类型
    BigInteger lowestKey = new BigInteger(startKey, 16);
    BigInteger highestKey = new BigInteger(endKey, 16);
    BigInteger range = highestKey.subtract(lowestKey);
    BigInteger regionIncrement = range.divide(BigInteger.valueOf(numRegions));
    lowestKey = lowestKey.add(regionIncrement);
    for(int i=0; i < numRegions-1;i++) {
        BigInteger key = lowestKey.add(regionIncrement.multiply(BigInteger.valueOf(i)));
        byte[] b = String.format("%016x", key).getBytes();
        splits[i] = b;
    }
    return splits;
}
 public void createTable() throws IOException {
   String startKeyString="0000000000000000";
   String endKeyString="ffffffffffffffff";
   byte[][] splitKeys= getHexSplits(startKeyString,endKeyString,10);
   admin.createTable(tableDescriptor,splitKeys);
}

运行代码,创建好的region所有的startKey、endKey都为16进制的字符串表示。
图片

ASCII表

图片

HBase 中,分区是一个很重要的概念,它可以提高 HBase 的性能和可伸缩性。分区是指在创建 HBase 表时,手动指定表的分区键,以便将数据分布到多个 Region 中。分区的目的是让数据分布均匀,避免某个 Region 过大而导致负载不均衡的情况。 分区的设计需要考虑以下几个因素: 1. 数据的访问模式:首先需要了解数据的访问模式,比如是否是范围查询、随机查询等,以便根据不同的访问模式来设计分区。 2. 数据的分布情况:需要了解数据的分布情况,比如数据的热点区域、数据的更新频率等,以便根据不同的分布情况来设计分区。 3. 期的数据量:需要估未来的数据量,以便根据数据量来设计分区。 4. 集群的硬件配置:需要了解集群的硬件配置,比如服务器的数量、内存大小、磁盘容量等,以便根据硬件配置来设计分区。 在设计分区时,可以采用以下几种策略: 1. 均匀分区:将表的分区键分成相等的若干部分,每个分区大小相等。 2. 范围分区:根据数据的范围来划分分区,比如按照时间范围来划分分区。 3. 哈希分区:根据分区键的哈希值来划分分区,可以确保数据分布均匀。 4. 混合分区:可以将多种分区策略组合起来使用,以便充分利用各种策略的优点。 需要注意的是,分区的设计需要根据实际情况进行调整和优化,以便达到最佳的性能和可伸缩性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值