package com.wenbronk.hbase; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.client.HBaseAdmin; import java.math.BigInteger; /** * hbase調優 */ public class UpperHbase { public static boolean createTable(HBaseAdmin admin, HTableDescriptor table, byte[][] splits) { try { // 添加二維數組對region進行查分 admin.createTable(table, splits); } catch (Exception e) { e.printStackTrace(); } return false; } /** * 獲取二維數組 */ public static byte[][] getHexSplits(String startKey, String endKey, int numRegions) { // //start:001,endkey:100,10region [001,010] [011,020] byte[][] splits = new byte[numRegions - 1][]; 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; } }
HBase中row key用來檢索表中的記錄,支持如下三種方式:html
在HBase中,row key能夠是任意字符串,最大長度64KB,實際應用中通常爲10~100bytes,存爲byte[]字節數組,通常設計成定長的。java
row key是按照字典序存儲,所以,設計row key時,要充分利用這個排序特色,將常常一塊兒讀取的數據存儲到一塊,將最近可能會被訪問的數據放在一塊。算法
舉個例子:若是最近寫入HBase表中的數據是最可能被訪問的,能夠考慮將時間戳做爲row key的一部分,因爲是字典序排序,因此可使用Long.MAX_VALUE - timestamp做爲row key,這樣能保證新寫入的數據在讀取時能夠被快速命中。apache
一、 大小越小越好編程
二、 值根據功能需求決定api
三、 Row最好有散列原則。數組
a) 取反 : 001 => 100, 002 => 200緩存
b) Hash: 經過hash算法進行散列安全
不要在一張表裏定義太多的column family(一個列族對應一個store, 有多個storeFile, 進行提交合並時, 全部的store一塊進行, 瞬間併發太大)。目前Hbase(2.x) 並不能很好的處理超過2~3個column family的表。由於某個column family在flush的時候,它鄰近的column family也會因關聯效應被觸發flush,最終致使系統產生更多的I/O。感興趣的同窗能夠對本身的HBase集羣進行實際測試,從獲得的測試結果數據驗證一下。網絡
建立表的時候,能夠經過HColumnDescriptor.setInMemory(true)將表放到RegionServer的緩存中,保證在讀取的時候被cache命中。
建立表的時候,能夠經過HColumnDescriptor.setMaxVersions(int maxVersions)設置表中數據的最大版本,若是隻須要保存最新版本的數據,那麼能夠設置setMaxVersions(1)。
建立表的時候,能夠經過HColumnDescriptor.setTimeToLive(int timeToLive)設置表中數據的存儲生命期,過時數據將自動被刪除,例如若是隻須要存儲最近兩天的數據,那麼能夠設置setTimeToLive(2 * 24 * 60 * 60)。
在HBase中,數據在更新時首先寫入WAL 日誌(HLog)和內存(MemStore)中,MemStore中的數據是排序的,當MemStore累計到必定閾值時,就會建立一個新的MemStore,而且將老的MemStore添加到flush隊列,由單獨的線程flush到磁盤上,成爲一個StoreFile。於此同時, 系統會在zookeeper中記錄一個redo point,表示這個時刻以前的變動已經持久化了(minor compact)。
StoreFile是隻讀的,一旦建立後就不能夠再修改。所以Hbase的更新實際上是不斷追加的操做。當一個Store中的StoreFile達到必定的閾值後,就會進行一次合併(major compact),將對同一個key的修改合併到一塊兒,造成一個大的StoreFile,當StoreFile的大小達到必定閾值後,又會對 StoreFile進行分割(split),等分爲兩個StoreFile。
因爲對錶的更新是不斷追加的,處理讀請求時,須要訪問Store中所有的StoreFile和MemStore,將它們按照row key進行合併,因爲StoreFile和MemStore都是通過排序的,而且StoreFile帶有內存中索引,一般合併過程仍是比較快的。
實際應用中,能夠考慮必要時手動進行major compact,將同一個row key的修改進行合併造成一個大的StoreFile。同時,能夠將StoreFile設置大些,減小split的發生。
hbase爲了防止小文件(被刷到磁盤的menstore)過多,以保證保證查詢效率,hbase須要在必要的時候將這些小的store file合併成相對較大的store file,這個過程就稱之爲compaction。在hbase中,主要存在兩種類型的compaction:minor compaction和major compaction。
minor compaction:的是較小、不多文件的合併。
major compaction 的功能是將全部的store file合併成一個,觸發major compaction的可能條件有:major_compact 命令、majorCompact() API、region server自動運行(相關參數:hbase.hregion.majoucompaction 默認爲24 小時、hbase.hregion.majorcompaction.jetter 默認值爲0.2 防止region server 在同一時間進行major compaction)。hbase.hregion.majorcompaction.jetter參數的做用是:對參數hbase.hregion.majoucompaction 規定的值起到浮動的做用,假如兩個參數都爲默認值24和0,2,那麼major compact最終使用的數值爲:19.2~28.8 這個範圍。
一、 關閉自動major compaction
二、 手動編程major compaction
Timer類,contab
minor compaction的運行機制要複雜一些,它由一下幾個參數共同決定:
hbase.hstore.compaction.min :默認值爲 3,表示至少須要三個知足條件的store file時,minor compaction纔會啓動
hbase.hstore.compaction.max 默認值爲10,表示一次minor compaction中最多選取10個store file
hbase.hstore.compaction.min.size 表示文件大小小於該值的store file 必定會加入到minor compaction的store file中
hbase.hstore.compaction.max.size 表示文件大小大於該值的store file 必定會被minor compaction排除
hbase.hstore.compaction.ratio 將store file 按照文件年齡排序(older to younger),minor compaction老是從older store file開始選擇
經過調用HTable.setAutoFlush(false)方法能夠將HTable寫客戶端的自動flush關閉,這樣能夠批量寫入數據到HBase,而不是有一條put就執行一次更新,只有當put填滿客戶端寫緩存時,才實際向HBase服務端發起寫請求。默認狀況下auto flush是開啓的。
經過調用HTable.setWriteBufferSize(writeBufferSize)方法能夠設置HTable客戶端的寫buffer大小,若是新設置的buffer小於當前寫buffer中的數據時,buffer將會被flush到服務端。其中,writeBufferSize的單位是byte字節數,能夠根據實際寫入數據量的多少來設置該值。
在HBae中,客戶端向集羣中的RegionServer提交數據時(Put/Delete操做),首先會先寫WAL(Write Ahead Log)日誌(即HLog,一個RegionServer上的全部Region共享一個HLog),只有當WAL日誌寫成功後,再接着寫MemStore,而後客戶端被通知提交數據成功;若是寫WAL日誌失敗,客戶端則被通知提交失敗。這樣作的好處是能夠作到RegionServer宕機後的數據恢復。
所以,對於相對不過重要的數據,能夠在Put/Delete操做時,經過調用Put.setWriteToWAL(false)或Delete.setWriteToWAL(false)函數,放棄寫WAL日誌,從而提升數據寫入的性能。
值得注意的是:謹慎選擇關閉WAL日誌,由於這樣的話,一旦RegionServer宕機,Put/Delete的數據將會沒法根據WAL日誌進行恢復。
經過調用HTable.put(Put)方法能夠將一個指定的row key記錄寫入HBase,一樣HBase提供了另外一個方法:經過調用HTable.put(List<Put>)方法能夠將指定的row key列表,批量寫入多行記錄,這樣作的好處是批量執行,只須要一次網絡I/O開銷,這對於對數據實時性要求高,網絡傳輸RTT高的情景下可能帶來明顯的性能提高。
hbase.client.scanner.caching配置項能夠設置HBase scanner一次從服務端抓取的數據條數,默認狀況下一次一條。經過將其設置成一個合理的值,能夠減小scan過程當中next()的時間開銷,代價是scanner須要經過客戶端的內存來維持這些被cache的行記錄。
有三個地方能夠進行配置:1)在HBase的conf配置文件中進行配置;2)經過調用HTable.setScannerCaching(int scannerCaching)進行配置;3)經過調用Scan.setCaching(int caching)進行配置。三者的優先級愈來愈高。
scan時指定須要的Column Family,能夠減小網絡傳輸數據量,不然默認scan操做會返回整行全部Column Family的數據。
經過scan取完數據後,記得要關閉ResultScanner,不然RegionServer可能會出現問題(對應的Server資源沒法釋放)。
HTable是HBase客戶端與HBase服務端通信的Java API對象,客戶端能夠經過HTable對象與服務端進行CRUD操做(增刪改查)。它的建立很簡單:
Configuration conf = HBaseConfiguration.create(); HTable table = new HTable(conf, "tablename"); //TODO CRUD Operation……
HTable使用時的一些注意事項:
1. 規避HTable對象的建立開銷
由於客戶端建立HTable對象後,須要進行一系列的操做:檢查.META.表確認指定名稱的HBase表是否存在,表是否有效等等,整個時間開銷比較重,可能會耗時幾秒鐘之長,所以最好在程序啓動時一次性建立完成須要的HTable對象,若是使用Java API,通常來講是在構造函數中進行建立,程序啓動後直接重用。
2. HTable對象不是線程安全的
HTable對象對於客戶端讀寫數據來講不是線程安全的,所以多線程時,要爲每一個線程單首創建複用一個HTable對象,不一樣對象間不要共享HTable對象使用,特別是在客戶端auto flash被置爲false時,因爲存在本地write buffer,可能致使數據不一致。
3. HTable對象之間共享Configuration
HTable對象共享Configuration對象,這樣的好處在於:
所以,與如下這種方式相比:
HTable table1 = new HTable("table1"); HTable table2 = new HTable("table2");
下面的方式更有效些:
Configuration conf = HBaseConfiguration.create(); HTable table1 = new HTable(conf, "table1"); HTable table2 = new HTable(conf, "table2");
備註:即便是高負載的多線程程序,也並無發現由於共享Configuration而致使的性能問題;若是你的實際狀況中不是如此,那麼能夠嘗試不共享Configuration。
HTablePool能夠解決HTable存在的線程不安全問題,同時經過維護固定數量的HTable對象,可以在程序運行期間複用這些HTable資源對象。
Configuration conf = HBaseConfiguration.create(); HTablePool pool = new HTablePool(conf, 10);
1. HTablePool能夠自動建立HTable對象,並且對客戶端來講使用上是徹底透明的,能夠避免多線程間數據併發修改問題。
2. HTablePool中的HTable對象之間是公用Configuration鏈接的,可以能夠減小網絡開銷。
HTablePool的使用很簡單:每次進行操做前,經過HTablePool的getTable方法取得一個HTable對象,而後進行put/get/scan/delete等操做,最後經過HTablePool的putTable方法將HTable對象放回到HTablePool中。
下面是個使用HTablePool的簡單例子:
public void createUser(String username, String firstName, String lastName, String email,
String password, String roles) throws IOException { HTable table = rm.getTable(UserTable.NAME); Put put = new Put(Bytes.toBytes(username)); put.add(UserTable.DATA_FAMILY, UserTable.FIRSTNAME, Bytes.toBytes(firstName)); put.add(UserTable.DATA_FAMILY, UserTable.LASTNAME, Bytes.toBytes(lastName)); put.add(UserTable.DATA_FAMILY, UserTable.EMAIL, Bytes.toBytes(email)); put.add(UserTable.DATA_FAMILY, UserTable.CREDENTIALS, Bytes.toBytes(password)); put.add(UserTable.DATA_FAMILY, UserTable.ROLES, Bytes.toBytes(roles)); table.put(put); table.flushCommits(); rm.putTable(table); }
Hbase和DBMS比較:
查詢數據不靈活:
1, 不能使用column之間過濾查詢
2, 支持全文索引。使用solr和hbase整合完成全文搜索。
a) 使用MR批量讀取hbase中的數據,在solr裏面創建索引(no store)之保存rowkey的值。
b) 根據關鍵詞從索引中搜索到rowkey(分頁)
c) 根據rowkey從hbase查詢全部數據