HBase(九)HBase表以及Rowkey的設計

一 命名空間

1 命名空間的結構

1) Table:表,全部的表都是命名空間的成員,即表必屬於某個命名空間,若是沒有指定, 則在 default 默認的命名空間中。正則表達式

2) RegionServer group:一個命名空間包含了默認的 RegionServer Group。算法

3) Permission:權限,命名空間可以讓咱們來定義訪問控制列表 ACL(Access Control List)。例如,建立表,讀取表,刪除,更新等等操做。shell

4) Quota:限額,能夠強制一個命名空間可包含的 region 的數量。(屬性:hbase.quota.enabled)apache

2 命名空間的使用

1) 建立命名空間api

hbase(main):002:0> create_namespace 'ns_school'

2) 建立表時指定命名空間數組

hbase(main):004:0> create 'ns_school:tbl_student','info'

3) 觀察 HDFS 中的目錄結構的變化 緩存

 

二. 建表高級屬性

  下面幾個 shell 命令在 hbase 操做中能夠起到很大的做用,且主要體如今建表的過程當中,看 下面幾個 create 屬性併發

1. BLOOMFILTER

  默認是 NONE 是否使用布隆過慮及使用何種方式,布隆過濾能夠每列族單獨啓用 使用 HColumnDescriptor.setBloomFilterType(NONE | ROW | ROWCOL) 對列族單獨啓用布隆app

  Default = ROW 對行進行布隆過濾負載均衡

  對 ROW,行鍵的哈希在每次插入行時將被添加到布隆

  對 ROWCOL,行鍵 + 列族 + 列族修飾的哈希將在每次插入行時添加到布隆

  使用方法: create 'table',{BLOOMFILTER =>'ROW'}

  做用:用布隆過濾能夠節省讀磁盤過程,能夠有助於下降讀取延遲

2. VERSIONS

  默認是 1 這個參數的意思是數據保留 1 個 版本,若是咱們認爲咱們的數據沒有這麼大 的必要保留這麼多,隨時都在更新,而老版本的數據對咱們毫無價值,那將此參數設爲 1 能 節約 2/3 的空間

  使用方法: create 'table',{VERSIONS=>'2'}

  附:MIN_VERSIONS => '0'是說在 compact 操做執行以後,至少要保留的版本

3. COMPRESSION

  默認值是 NONE 即不使用壓縮,這個參數意思是該列族是否採用壓縮,採用什麼壓縮算 法,方法: create 'table',{NAME=>'info',COMPRESSION=>'SNAPPY'} ,建議採用 SNAPPY 壓縮算 法 ,HBase 中,在 Snappy 發佈以前(Google 2011 年對外發布 Snappy),採用的 LZO 算法,目標是達到儘量快的壓縮和解壓速度,同時減小對 CPU 的消耗;

  在 Snappy 發佈以後,建議採用 Snappy 算法(參考《HBase: The Definitive Guide》),具體 能夠根據實際狀況對 LZO 和 Snappy 作過更詳細的對比測試後再作選擇。

  若是建表之初沒有壓縮,後來想要加入壓縮算法,能夠經過 alter 修改 schema

4. TTL

  默認是 2147483647 即:Integer.MAX_VALUE 值大概是 68 年,這個參數是說明該列族數據的存活時間,單位是 s

  這個參數能夠根據具體的需求對數據設定存活時間,超過存過期間的數據將在表中不在 顯示,待下次 major compact 的時候再完全刪除數據

  注意的是 TTL 設定以後 MIN_VERSIONS=>'0' 這樣設置以後,TTL 時間戳過時後,將所有 完全刪除該 family 下全部的數據,若是 MIN_VERSIONS 不等於 0 那將保留最新的 MIN_VERSIONS 個版本的數據,其它的所有刪除,好比 MIN_VERSIONS=>'1' 屆時將保留一個 最新版本的數據,其它版本的數據將再也不保存。

5. alter

使用方法:

  如 修改壓縮算法

disable 'table'
alter 'table',{NAME=>'info',COMPRESSION=>'snappy'}
enable 'table'

  可是須要執行 major_compact 'table' 命令以後 纔會作實際的操做。 

6. describe/desc

  這個命令查看了 create table 的各項參數或者是默認值。

  使用方式:describe 'user_info'

7. disable_all/enable_all

  disable_all 'toplist.*' disable_all 支持正則表達式,並列出當前匹配的表的以下:

 toplist_a_total_1001
 toplist_a_total_1002
 toplist_a_total_1008
 toplist_a_total_1009
 toplist_a_total_1019
 toplist_a_total_1035
 ...
 Disable the above 25 tables (y/n)? 並給出確認提示

8. drop_all

  這個命令和 disable_all 的使用方式是同樣的

9. hbase 預分區

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

命令方式:

# create table with specific split points
hbase>create 'table1','f1',SPLITS => ['\x10\x00', '\x20\x00', '\x30\x00', '\x40\x00']
# create table with four regions based on random bytes keys
hbase>create 'table2','f1', { NUMREGIONS => 8 , SPLITALGO => 'UniformSplit' }
# create table with five regions based on hex keys
hbase>create 'table3','f1', { NUMREGIONS => 10, SPLITALGO => 'HexStringSplit' } 

  也可使用 api 的方式:

hbase org.apache.hadoop.hbase.util.RegionSplitter test_table HexStringSplit -c 10 -f info

hbase org.apache.hadoop.hbase.util.RegionSplitter splitTable HexStringSplit -c 10 -f info

參數:

  test_table 是表名

  HexStringSplit 是split 方式

  -c 是分 10 個 region

  -f 是 family

可在 UI 上查看結果,如圖:

  這樣就能夠將表預先分爲 15 個區,減小數據達到 storefile 大小的時候自動分區的時間 消耗,而且還有以一個優點,就是合理設計 rowkey 能讓各個 region 的併發請求平均分配(趨 於均勻) 使 IO 效率達到最高,可是預分區須要將 filesize 設置一個較大的值,設置哪一個參數 呢 hbase.hregion.max.filesize 這個值默認是 10G 也就是說單個 region 默認大小是 10G

  這個參數的默認值在 0.90 到 0.92 到 0.94.3 各版本的變化:256M--1G--10G

  可是若是 MapReduce Input 類型爲 TableInputFormat 使用 hbase 做爲輸入的時候,就要注意 了,每一個 region 一個 map,若是數據小於 10G 那隻會啓用一個 map 形成很大的資源浪費, 這時候能夠考慮適當調小該參數的值,或者採用預分配 region 的方式,並將檢測若是達到 這個值,再手動分配 region。

三. 表設計

1. 列簇設計

  追求的原則是:目前Hbase官方建議不超過2~3個column family。由於某個column family在flush的時候,它鄰近的column family也會因關聯效應被觸發flush,最終致使系統產生更多的I/O。

  最優設計是:將全部相關性很強的 key-value 都放在同一個列簇下,這樣既能作到查詢效率 最高,也能保持儘量少的訪問不一樣的磁盤文件。

  以用戶信息爲例,能夠將必須的基本信息存放在一個列族,而一些附加的額外信息能夠放在 另外一列族。

2. 預分區

每個region 維護着startRow 與endRowKey,若是加入的數據符合某個region 維護的rowKey範圍,則該數據交給這個 region 維護。那麼依照這個原則,咱們能夠將數據索要投放的分區提早大體的規劃好,以提升 HBase 性能。

1) 手動設定預分區

hbase> create 'staff','info','partition1',SPLITS => ['1000','2000','3000','4000']

2) 生成 16 進制序列預分區

create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}

3) 按照文件中設置的規則預分區

建立 splits.txt 文件內容以下:

aaaa 
bbbb
cccc dddd

而後執行:

create 'staff3','partition3',SPLITS_FILE => 'splits.txt'

4) 使用 JavaAPI 建立預分區

//自定義算法,產生一系列 Hash 散列值存儲在二維數組中
byte[][] splitKeys = 某個散列值函數
//建立 HBaseAdmin 實例
HBaseAdmin hAdmin = new HBaseAdmin(HBaseConfiguration.create());
//建立 HTableDescriptor 實例
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
//經過 HTableDescriptor 實例和散列值二維數組建立帶有預分區的 HBase 表
hAdmin.createTable(tableDesc, splitKeys);

3. RowKey 設計

  HBase 中,表會被劃分爲 1...n 個 Region,被託管在 RegionServer 中。Region 二個重要屬性:StartKey 與 EndKey 表示這個 Region 維護的 rowKey 範圍,當咱們要讀/寫數據時,若是 rowKey 落在某個 start-end key 範圍內,那麼就會定位到目標 region 而且讀/寫到相關的數據,那怎麼快速精準的定位到咱們想要操做的數據,就在於咱們的 rowkey 的設計了。

四. Rowkey 設計三原則

        一條數據的惟一標識就是 rowkey,那麼這條數據存儲於哪一個分區,取決於 rowkey 處於哪一個一個預分區的區間內,設計 rowkey 的主要目的 ,就是讓數據均勻的分佈於全部的 region 中,在必定程度上防止數據傾斜。接下來咱們就談一談 rowkey 經常使用的設計方案。

1. rowkey 長度原則

  Rowkey 是一個二進制碼流,Rowkey 的長度被不少開發者建議說設計在 10~100 個字節,不過建議是越短越好,不要超過 16 個字節,存爲byte[]字節數組,通常設計成定長的

  緣由以下:

    一、數據的持久化文件 HFile 中是按照 KeyValue 存儲的,若是 Rowkey 過長好比 100 個字 節,1000 萬列數據光 Rowkey 就要佔用 100*1000 萬=10 億個字節,將近 1G 數據,這會極大 影響 HFile 的存儲效率;

    二、MemStore 將緩存部分數據到內存,若是 Rowkey 字段過長內存的有效利用率會下降, 系統將沒法緩存更多的數據,這會下降檢索效率。所以 Rowkey 的字節長度越短越好。

    三、目前操做系統是都是 64 位系統,內存 8 字節對齊。控制在 16 個字節,8 字節的整數 倍利用操做系統的最佳特性。

2. rowkey 散列原則

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

      row key是按照字典序存儲,所以,設計row key時,要充分利用這個排序特色,將常常一塊兒讀取的數據存儲到一塊,將最近可能會被訪問的數據放在一塊。

舉個例子:若是最近寫入HBase表中的數據是最可能被訪問的,能夠考慮將時間戳做爲row key的一部分,因爲是字典序排序,因此可使用Long.MAX_VALUE - timestamp做爲row key,這樣能保證新寫入的數據在讀取時能夠被快速命中。

3. rowkey 惟一原則

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

五. 數據熱點

  HBase 中的行是按照 rowkey 的字典順序排序的,這種設計優化了 scan 操做,能夠將相 關的行以及會被一塊兒讀取的行存取在臨近位置,便於 scan。然而糟糕的 rowkey 設計是熱點 的源頭。 熱點發生在大量的 client 直接訪問集羣的一個或極少數個節點(訪問多是讀, 寫或者其餘操做)。大量訪問會使熱點 region 所在的單個機器超出自身承受能力,引發性能 降低甚至 region 不可用,這也會影響同一個 RegionServer 上的其餘 region,因爲主機沒法服 務其餘 region 的請求。 設計良好的數據訪問模式以使集羣被充分,均衡的利用。 爲了不寫熱點,設計 rowkey 使得不一樣行在同一個 region,可是在更多數據狀況下,數據 應該被寫入集羣的多個 region,而不是一個。

防止數據熱點的有效措施

  加鹽

  這裏所說的加鹽不是密碼學中的加鹽,而是在 rowkey 的前面增長隨機數,具體就是給 rowkey 分配一個隨機前綴以使得它和以前的 rowkey 的開頭不一樣。分配的前綴種類數量應該 和你想使用數據分散到不一樣的 region 的數量一致。加鹽以後的 rowkey 就會根據隨機生成的 前綴分散到各個 region 上,以免熱點。

例:生成隨機數、hash、散列值

好比:
本來 rowKey 爲 1001 的,SHA1 後變成:dd01903921ea24941c26a48f2cec24e0bb0e8cc7 
本來 rowKey 爲 3001 的,SHA1 後變成:49042c54de64a1e9bf0b33e00245660ef92dc7bd
本來 rowKey 爲 5001 的,SHA1 後變成:7b61dec07e02c188790670af43e717f0f46e8913 在作此操做以前,通常咱們會選擇從數據集中抽取樣本,來決定什麼樣的 rowKey 來 Hash 後做爲每一個分區的臨界值。

  哈希

  哈希會使同一行永遠用一個前綴加鹽。哈希也可使負載分散到整個集羣,可是讀倒是 能夠預測的。使用肯定的哈希可讓客戶端重構完整的 rowkey,可使用 get 操做準確獲取 某一個行數據

  反轉

  第三種防止熱點的方法是反轉固定長度或者數字格式的 rowkey。這樣可使得 rowkey 中常常改變的部分(最沒有意義的部分)放在前面。這樣能夠有效的隨機 rowkey,可是犧牲了 rowkey 的有序性。

  反轉 rowkey 的例子以手機號爲 rowkey,能夠將手機號反轉後的字符串做爲 rowkey,這 樣的就避免了以手機號那樣比較固定開頭致使熱點問題

例: 字符串反轉

20170524000001 轉成 10000042507102
20170524000002 轉成 20000042507102

這樣也能夠在必定程度上散列逐步 put 進來的數據。

例: 字符串拼接

20170524000001_a12e
20170524000001_93i7

  時間戳反轉

  一個常見的數據處理問題是快速獲取數據的最近版本,使用反轉的時間戳做爲 rowkey 的一部分對這個問題十分有用,能夠用 Long.Max_Value - timestamp 追加到 key 的末尾,例 如 [key][reverse_timestamp] , [key] 的最新值能夠經過 scan [key]得到[key]的第一條記錄,因 爲 HBase 中 rowkey 是有序的,第一條記錄是最後錄入的數據。好比須要保存一個用戶的操 做記錄,按照操做時間倒序排序,在設計 rowkey 的時候,能夠這樣設計 [userId 反轉][Long.Max_Value - timestamp],在查詢用戶的全部操做記錄數據的時候,直接指 定 反 轉 後 的 userId , startRow 是 [userId 反 轉 ][000000000000],stopRow 是 [userId 反 轉][Long.Max_Value - timestamp]

  若是須要查詢某段時間的操做記錄,startRow 是[user 反轉][Long.Max_Value - 起始時間], stopRow 是[userId 反轉][Long.Max_Value - 結束時間]

相關文章
相關標籤/搜索