Hbase優化之預分割槽
如果在hbase shell中使用create建表時只寫了表名和列族名,那麼這張表將只有一個region ,當一個region的大小超過閾值時會自動split成兩個,但split操作會帶來資源消耗。region個數太少時,在資料量大、訪問量大,或被批處理程式讀寫情況下效能可能會很差,並且伴隨大批量讀寫而來的GC可能會使regionserver宕機,接著region被transit到其他節點上,將逐步拖垮Hbase叢集上的所有節點。
所以推薦在建表時進行預分割槽,充分考慮rowkey的分佈做出合理的預分割槽方案。要考慮的點包括region的個數、region的大小等。
- region的個數
如果使用MapReduce讀取Hbase表資料,Map的個數等於該表region的個數,每個region都會有一個單獨程序來處理,這個程序會逐條處理region中的每一行資料。舉例來說如果只有一個region,那麼讀取資料的就只有一個程序;如果拆成10個數據均勻分佈的region,那麼10個map會帶來10倍的效率提升。
大資料量情況下越發需要並行處理,因此我們往往希望源表的region的個數多一些。但是同時也要考慮叢集的承載能力,Hbase的region個數上限可以參考官網給出的如下公式,其中RS Xmx是regionserver的記憶體堆疊大小,官網建議每臺20~24或更小,因為過大的記憶體會導致GC時間過長(GC方式從CMS改為G1後可以增大該值,機器記憶體足夠的情況下可以翻倍甚至更大)。
((RS Xmx) *hbase.regionserver.global.memstore.size) / (hbase.hregion.memstore.flush.size *(# column families))。
即24G*0.45/128M=86.4個,在實際使用中很容易超過這個值。另外官網建議每個RS 20~200個regions是比較合理的。因此region個數也不是越多越好,還要考慮叢集情況。我們可以在Hbase WebUI上看到這個值。
對於不需要用MR批量讀Hbase表,相比需要MR讀的表region個數可以少一些,以此來控制regionserver上region總數。
- region的大小
單個region最大大小官方推薦5~10GB,這是三備份前的資料大小,通過hbase.hregion.max.filesize配置,當超過這個值後region會split,估計好資料量併合理的劃分region會減少不必要的效能損失。甚至設定足夠大的值,日常監控中發現過大後手工做split。
- 預分割槽的方法
預建region可以在shell中或者程式中實現,網上很多文章,如下是一些例子,不再贅述。要想清楚rowkey的邊界,比如對於全部都是數字開頭的rowkey,分200個region,邊界就是000,005,010……995。
hbase> create 't1', 'f1', SPLITS => ['10', '20', '30', '40']
hbase> create 't1', 'f1', SPLITS_FILE => 'splits.txt'
hbase> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
private void createTable() {
HBaseAdmin admin = null;
String tableName="NewTable";
String columnFamilyName="cf";
try {
Configuration conf = HBaseConfiguration.create();
admin = new HBaseAdmin(conf);
TableName tableNameV = TableName.valueOf(tableName);
if (admin.tableExists(tableNameV)) {
System.out.println("Table " + tableName + " already exist.");
return;
}
HTableDescriptor tableDesc = new HTableDescriptor(tableNameV);
HColumnDescriptor columnDesc = new HColumnDescriptor(columnFamilyName);
tableDesc.addFamily(columnDesc);
admin.createTable(tableDesc, splits());
System.out.println("Created table : " + tableName + " successfully.");
} catch (Exception e) {
System.out.println("Failed create table " + tableName+ e.toString());
}
}
private static byte[][] splits() {}