hbase預分區(qū)誤區(qū)

預拆分表通常是一種比較好的實踐。如果預先拆分表,則必須了解rowkey將如何分布在region邊界上。是否所有的region都有rowkey能夠訪問。
舉一個為什么這一點很重要的示例,考慮使用可顯示十六進制字符作為鍵的前導位置的示例(例如,“0000000000000000”到“ffffffffffffffff”)。

創(chuàng)建預分區(qū)表的代碼:

String startKeyString="0000000000000000";
String endKeyString="ffffffffffffffff";
int numRegions=10;
admin.createTable(tableDescriptor,startKeyString.getBytes(),endKeyString.getBytes(),numRegions);
//創(chuàng)建表,與下面的方法等價,生成一樣region分布的表
int splitTimes=numRegions-3;//因為有兩個空數(shù)組作為rowkey的開始和結(jié)束rowkey
byte[][] splitKeys=Bytes.split(startKeyString.getBytes(),endKeyString.getBytes(),splitTimes);
admin.createTable(tableDescriptor,splitKeys);

執(zhí)行代碼后,table的region分布如下:


image.png

當我們使用十六進制的字符作為rowkey前綴的時候,就會發(fā)生熱點分區(qū)的問題。記錄都會集中在如下四個region,因為十六進制字符rowkey的值區(qū)間為[0-9]和[a-f]。


image.png

做一些驗證,向表中插入數(shù)據(jù)

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);

發(fā)現(xiàn)如下region接收到一次請求,即"a"與各個region的start key首字母與end key比較,"a"會落在"_"與"f"之間,所以此條記錄會落在次rowkey區(qū)間的region上。


image.png

另外需要注意的一點,rowkey字符串的每個字節(jié)的ascii碼會與startkey、endkey的每一個字節(jié)的ascii碼比較。

/** 因為最后一個字符"e"的ascii碼比"f"小,所以仍然落在split key "ffffffffffffffff"之前的region分區(qū) */
byte[] rowKey=Bytes.toBytes("fffffffffffffffe");

/** 因為沒有第三個字符與split key比較,所以"ff"小于"ffffffffffffffff" */
byte[] rowKey=Bytes.toBytes("ff");

上面兩個rowkey的記錄仍然會落在如下分區(qū)


image.png

錯誤分區(qū)代碼總結(jié):

  1. 預拆分表通常是一種最佳實踐。但您需要讓預拆分表滿足rokwey可以訪問任意的分區(qū)。雖然這個例子演示了十六進制鍵空間的問題,但任何鍵空間都可能發(fā)生同樣的問題。需要了解你的數(shù)據(jù),要知道rowkey的展示格式,知道各個region的start key與end key的展示格式。
  2. 錯誤的分區(qū)代碼盡管不適合,但只要加以改進,將所有創(chuàng)建的region都可以在rowkey鍵空間中訪問,那么使用十六進制鍵(更常見的目的是,可顯示rowkey數(shù)據(jù))仍然可以作為預拆分表的方案。

下面是如何為十六進制格式的rowkey進行預分區(qū)的正確示例:

public static byte[][] getHexSplits(String startKey, String endKey, int numRegions) {
    byte[][] splits = new byte[numRegions-1][];
    //將16進制表示的字符串startKey轉(zhuǎn)換為10進制的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 createTable3() throws IOException {
   String startKeyString="0000000000000000";
   String endKeyString="ffffffffffffffff";
   byte[][] splitKeys= getHexSplits(startKeyString,endKeyString,10);
   admin.createTable(tableDescriptor,splitKeys);
}

運行代碼,創(chuàng)建好的region所有的startKey、endKey都為16進制的字符串表示。


image.png

ASCII表:


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容