1. 程式人生 > >Hbase優化之預分割槽

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() {}