HBase表設計及優化點

1 在創建表的時候指定分區

​默認情況下,在創建HBase表的時候會自動創建一個region分區,當導入數據的時候,所有的HBase客戶端都向這一個region寫數據,直到這個region足夠大了才進行切分。一種可以加快批量寫入速度的方法是通過預先創建一些空的regions,這樣當數據寫入HBase時,會按照region分區情況,在集羣內做數據的負載均衡。

  • 實現方式是使用admin對象的切分策略
byte[] startKey = ...;      // your lowest key
byte[] endKey = ...;        // your highest key
int numberOfRegions = ...;  // number of regions to create
admin.createTable(table, startKey, endKey, numberOfRegions);
  • 用戶自定義切片
byte[][] splits = ...;   // create your own splits
/*
byte[][] splits = new byte[][] { Bytes.toBytes("100"),
                Bytes.toBytes("200"), Bytes.toBytes("400"),
                Bytes.toBytes("500") };
*/
admin.createTable(table, splits);

2 Rowkey設計

​HBase中rowkey用來檢索表中的記錄,支持以下三種方式:

​1. 通過單個rowkey訪問:即按照某個rowkey鍵值進行get操作;

​2. 通過rowkey的range進行scan:即通過設置startRowKey和endRowKey,在這個範圍內進行掃描;

​3. 全表掃描:即直接掃描整張表中所有行記錄

​在HBase中,rowkey可以是任意字符串,最大長度64KB,實際應用中一般爲10~100字節,存爲byte[]字節數組,一般設計成定長的。

rowkey是按照字典序存儲,因此,設計rowkey時,要充分利用這個排序特點,將經常一起讀取的數據存儲到一塊,將最近可能會被訪問的數據放在一塊。

​Rowkey設計原則:

​1. 越短越好,提高效率

​* 數據的持久化文件HFile中是按照KeyValue存儲的,如果rowkey過長,比如超過100字節,1000萬行數據,單單是存儲rowkey的數據就要佔用10億個字節,將近1G數據,這樣會影響HFile的存儲效率。

​* HBase中包含緩存機制,每次會將查詢的結果暫時緩存到HBase的內存中,如果rowkey字段過長,內存的利用率就會降低,系統不能緩存更多的數據,這樣會降低檢索效率。

​2. 散列原則——實現負載均衡

​如果rowkey是按時間戳的方式遞增,不要將時間放在二進制碼的前面,建議將rowkey的高位作爲散列字段,由程序循環生成,低位放時間字段,這樣將提高數據均衡分佈在每個regionserver實現負載均衡的機率。如果沒有散列字段,首字段直接是時間信息將產生所有新數據都在一個 regionServer上堆積的熱點現象,這樣在做數據檢索的時候負載將會集中在個別regionServer,降低查詢效率。

​* 加鹽:添加隨機值

​* hash:採用md5散列算法取前4位做前綴

​* 反轉:將手機號反轉(儘量將具有隨機性的字符串放在前面,這樣便於Hash

​3. 唯一原則–字典序排序存儲

​必須在設計上保證其唯一性,rowkey是按照字典順序排序存儲的,因此,設計rowkey的時候,要充分利用這個排序的特點,將經常讀取的數據存儲到一塊,將最近可能會被訪問的數據放到一塊。

3 列族的設計

​不要在一張表裏定義太多的column family。目前Hbase並不能很好的處理超過2~3column family的表。因爲某個column familyflush的時候,它鄰近的column family也會因關聯效應被觸發flush,最終導致系統產生更多的I/O

​原因:

​1. 當開始向hbase中插入數據的時候,數據會首先寫入到memstore,而memstore是一個內存結構,每個列族對應一個memstore,當包含更多的列族的時候,會導致存在多個memstore,每一個memstoreflush的時候會對應一個hfile的文件,因此會產生很多的hfile文件,更加嚴重的是,flush操作時region級別,當region中的某個memstoreflush的時候,同一個region的其他memstore也會進行flush操作,當某一張表擁有很多列族的時候,且列族之間的數據分佈不均勻的時候,會產生更多的磁盤文件;

​2. 當hbase表的某個region過大,會被拆分成兩個,如果我們有多個列族,且這些列族之間的數據量相差懸殊的時候,regionsplit操作會導致原本數據量小的文件被進一步的拆分,而產生更多的小文件;

​3. 與Flush操作一樣,目前HBaseCompaction操作也是Region級別的,過多的列族也會產生不必要的I/O;

​4. HDFS其實對一個目錄下的文件數有限制的(dfs.namenode.fs-limits.max-directory-items)。如果我們有N個列族,MRegion,那麼我們持久化到HDFS至少會產生N*M個文件,而每個列族對應底層的HFile文件往往不止一個,我們假設爲K個,那麼最終表在HDFS目錄下的文件數將是N*M*K,這可能會操作HDFS的限制。

4 in memory

hbaseLRU緩存基礎之上採用了分層設計,整個blockcache分成了三個部分,分別是singlemultiinMemory

​三者區別如下:

  • single:如果一個block第一次被訪問,放在該優先隊列中;
  • multi:如果一個block被多次訪問,則從single隊列轉移到multi隊列
  • inMemory:優先級最高,常駐cache,因此一般只有hbase系統的元數據,如meta表之類的纔會放到inMemory隊列中。

5 保存版本數

​創建表的時候,可以通過ColumnFamilyDescriptorBuilder.setMaxVersions(int maxVersions)設置表中數據的最大版本,如果只需要保存最新版本的數據,那麼可以設置setMaxVersions(1),保留更多的版本信息會佔用更多的存儲空間。

6 設置最大保存時間

​創建表的時候,可以通過ColumnFamilyDescriptorBuilder.setTimeToLive(int timeToLive)設置表中數據的存儲生命期,過期數據將自動被刪除,例如如果只需要存儲最近兩天的數據,那麼可以設置setTimeToLive(2 * 24 * 60 * 60)

7 合併操作

​在HBase中,數據在更新時首先寫入WAL 日誌(HLog)和內存(MemStore)中,MemStore中的數據是排序的,當MemStore累計到一定閾值(64MB)時,就會創建一個新的MemStore,並且將老的MemStore添加到flush隊列,由單獨的線程flush到磁盤上,成爲一個StoreFile。於此同時, 系統會在zookeeper中記錄一個redo point,表示這個時刻之前的變更已經持久化了。

StoreFile是隻讀的,一旦創建後就不可以再修改。因此Hbase的更新其實是不斷追加的操作。當一個Store中的StoreFile達到一定的閾值後,就會進行一次合併,將對同一個key的修改合併到一起,形成一個大的StoreFile,當StoreFile的大小達到一定閾值後,又會對StoreFile進行分割,等分爲兩個StoreFile

​由於對錶的更新是不斷追加的,處理讀請求時,需要訪問Store中全部的StoreFileMemStore,將它們按照row key進行合併,由於StoreFileMemStore都是經過排序的,並且StoreFile帶有內存中索引,通常合併過程還是比較快的。

實際應用中,可以考慮必要時手動進行major compact,將同一個row key的修改進行合併形成一個大的StoreFile。同時,可以將StoreFile設置大些,減少split的發生。

hbase爲了防止小文件(被刷到磁盤的menstore)過多,以保證保證查詢效率,hbase需要在必要的時候將這些小的store file合併成相對較大的store file,這個過程就稱之爲compaction。在hbase中,主要存在兩種類型的compactionminor compactionmajor compaction

​1. minor compaction: 的是較小、很少文件的合併。minor compaction的運行機制要複雜一些,它由一下幾個參數共同決定:

hbase.hstore.compaction.min: 默認值爲3,表示至少需要三個滿足條件的store file時,minor compaction纔會啓動

hbase.hstore.compaction.max: 默認值爲10,表示一次minor compaction中最多選取10store file

hbase.hstore.compaction.min.size: 表示文件大小小於該值的store file一定會加入到minor compactionstore file

hbase.hstore.compaction.max.size: 表示文件大小大於該值的store file一定不會被添加到minor compaction

hbase.hstore.compaction.ratio: 將StoreFile按照文件年齡排序,minor compaction總是從older store file開始選擇,如果該文件的size小於後面hbase.hstore.compaction.maxstore file size之和乘以ratio的值,那麼該store file將加入到minor compaction中。如果滿足minor compaction條件的文件數量大於hbase.hstore.compaction.min,纔會啓動。

​2. major compaction的功能是將所有的store file合併成一個,觸發major compaction的可能條件有:

major_compact命令

majorCompact() API

region server運行

hbase.hregion.majorcompaction默認爲24小時

hbase.hregion.majorcompaction.jetter默認值爲0.2爲防止region server在同一時間進行major compaction

hbase.hregion.majorcompaction.jetter參數的作用是:對參數hbase.hregion.majorcompaction規定的值起到浮動的作用,假如兩個參數都爲默認值240.2,那麼major compact最終使用的數值爲19.2~28.8這個範圍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章