hbase存儲格式
『壹』 Hbase和傳統資料庫的區別
HBase與傳統關系資料庫的區別?
答:主要體現在以下幾個方面:1.數據類型。關系資料庫採用關系模型,具有豐富的數據類型和儲存方式。HBase則採用了更加簡單的數據模型,它把數據儲存為未經解釋的字元串,用戶可以把不同格式的結構化數據和非結構化數據都序列化成字元串保存到HBase中,用戶需要自己編寫程序把字元串解析成不同的數據類型。
2.數據操作。關系資料庫中包含了豐富的操作,如插入、刪除、更新、查詢等,其中會涉及復雜的多表連接,通常是藉助多個表之間的主外鍵關聯來實現的。HBase操作則不存在復雜的表與表之間的關系,只有簡單的插入、查詢、刪除、清空等,因為HBase在設計上就避免了復雜的表與表之間的關系,通常只採用單表的主鍵查詢,所以它無法實現像關系資料庫中那樣的表與表之間的連接操作。
3.存儲模式。關系資料庫是基於行模式存儲的,元祖或行會被連續地存儲在磁碟頁中。在讀取數據時,需要順序掃描每個元組,然後從中篩選出查詢所需要的屬性。如果每個元組只有少量屬性的值對於查詢是有用的,那麼基於行模式存儲就會浪費許多磁碟空間和內存帶寬。HBase是基於列存儲的,每個列族都由幾個文件保存,不同列族的文件是分離的,它的優點是:可以降低I/O開銷,支持大量並發用戶查詢,因為僅需要處理可以回答這些查詢的列,而不是處理與查詢無關的大量數據行;同一個列族中的數據會被一起進行壓縮,由於同一列族內的數據相似度較高,因此可以獲得較高的數據壓縮比。
4.數據索引。關系資料庫通常可以針對不同列構建復雜的多個索引,以提高數據訪問性能。與關系資料庫不同的是,HBase只有一個索引——行鍵,通過巧妙的設計,HBase中所有訪問方法,或者通過行鍵訪問,或者通過行鍵掃描,從而使整個系統不會慢下來。由於HBase位於Hadoop框架之上,因此可以使用Hadoop MapRece來快速、高效地生成索引表。
6.數據維護。在關系資料庫中,更新操作會用最新的當前值去替換記錄中原來的舊值,舊值被覆蓋後就不會存在。而在HBase中執行更新操作時,並不會刪除數據舊的版本,而是生成一個新的版本,舊有的版本仍舊保留。
7.可伸縮性。關系資料庫很難實現橫向擴展,縱向擴展的空間也比較有限。相反,HBase和BigTable這些分布式資料庫就是為了實現靈活的水平擴展而開發的,因此能夠輕易地通過在集群中增加或者減少硬體數量來實現性能的伸縮。
但是,相對於關系資料庫來說,HBase也有自身的局限性,如HBase不支持事務,因此無法實現跨行的原子性。
註:本來也想來問這個問題,然後復制一下的。結果找不到,只好自己手打了,麻煩復制拿去用的同學點下贊唄。
『貳』 【HBase】HBase 自動拆分和預分區
[TOC]
HBase 中,表會被劃分為1...n 個 Region,被託管在 RegionServer 中。
Region 二個重要的屬性:StartKey 與 EndKey 表示這個 Region 維護的 RowKey 范圍,當讀/寫數據時,如果 RowKey 落在某個 start-end key 范圍內,那麼就會定位到目標region並且讀/寫到相關的數據。
默認,HBase 在創建表的時候,會自動為表分配一個 Region,正處於混沌時期,start-end key 無邊界,所有 RowKey 都往這個 Region里分配。
當數據越來越多,Region 的 size 越來越大時,達到默認的閾值時(根據不同的拆分策略有不同的閾值),HBase 中該 Region 將會進行 split,會找到一個 MidKey 將 Region 一分為二,成為 2 個 Region。而 MidKey 則為這二個 Region 的臨界,左為 N 無下界,右為 M 無上界。< MidKey 被分配到 N 區,> MidKey 則會被分配到 M 區。
隨著數據量進一步擴大,分裂的兩個 Region 達到臨界後將重復前面的過程,分裂出更多的 Region。
Region 的分割操作是不可見的,Master 不會參與其中。RegionServer 拆分 Region的步驟是:先將該 Region 下線,然後拆分,將其子 Region 加入到 META 元信息中,再將他們加入到原本的 RegionServer 中,最後匯報 Master。
執行 split 的線程是 CompactSplitThread。
在 2.0.5 版本中,HBase 提供了 7 種自動拆分策略:
他們之間的繼承關系如下:
有三種配置方法:
0.94.0 之前的默認拆分策略,這種策略非常簡單,只要 Region 中的任何一個 StoreFile 的大小達到了 hbase.hregion.max.filesize 所定義的大小 ,就進行拆分。
1)相關參數:
hbase.hregion.max.filesize
2)部分源碼 :
拆分的閾值大小可在創建表的時候設置,如果沒有設置,就取 hbase.hregion.max.filesize 這個配置定義的值,如果這個配置也沒有定義,取默認值 10G。
3)拆分效果:
經過這種策略的拆分後,Region 的大小是均勻的,例如一個 10G 的Region,拆分為兩個 Region 後,這兩個新的 Region 的大小是相差不大的,理想狀態是每個都是5G。
**ConstantSizeRegionSplitPolicy **切分策略對於大表和小表沒有明顯的區分,閾值(hbase.hregion.max.filesize):
4)創建表時配置:
該策略繼承自 ConstantSizeRegionSplitPolicy,是 0.94.0 到 2.0.0 版本的默認策略,其 優化了原來 ConstantSizeRegionSplitPolicy 只是單一按照 Region 文件大小的拆分策略,增加了對當前表的分片數作為判斷因子 。當Region中某個 Store Size 達到 sizeToCheck 閥值時進行拆分,sizeToCheck 計算如下:
如果表的分片數為 0 或者大於 100,則切分大小還是以設置的單一 Region 文件大小為標准。如果分片數在 1~99 之間,則由 min(單一 Region 大小, Region 增加策略的初始化大小 * 當前 Table Region 數的3次方) 決定 。
Region 增加策略的初始化大小計算如下:
1)相關參數:
hbase.hregion.max.filesize
hbase.increasing.policy.initial.size
hbase.hregion.memstore.flush.size
2)部分源碼:
在默認情況,使用 策略拆分 Region 的過程是:
3)拆分效果:
和 ConstantSizeRegionSplitPolicy 一樣,也是均勻拆分。
不同的是, 切分策略彌補了ConstantSizeRegionSplitPolicy 的短板,能夠自適應大表和小表,並且在大集群條件下對於很多大表來說表現很優秀。
但並不完美,這種策略下很多小表會在大集群中產生大量小 Region,分散在整個集群中。而且在發生 Region 遷移時也可能會觸發 Region 分裂。
4)創建表時配置:
2.0 版本默認切分策略。SteppingSplitPolicy 是 的子類,其對 Region 拆分文件大小做了優化,如果只有1個 Region 的情況下,那第1次的拆分就是 256M,後續則按配置的拆分文件大小(10G)做為拆分標准。
1)相關參數:
同 。
2)全部源碼:
它的源碼只有一個方法,優化了 getSizeToCheck 方法,其他都是繼承 自 類。
3)拆分效果:
在 策略中,針對大表的拆分表現很不錯,但是針對小表會產生過多的 Region,SteppingSplitPolicy 則將小表的 Region 控制在一個合理的范圍,對大表的拆分也不影響。
4)創建表時配置:
KeyPrefixRegionSplitPolicy 是 的子類,該策略除了具備其父類自動調整 Region 拆分閾值大小、適應大小表的特點外,增加了對拆分點(splitPoint,拆分點就是 Region 被拆分處的 RowKey)的定義,可以保證有相同前綴的 RowKey不會被拆分到兩個不同的 Region 裡面。
1)相關參數:
在 的配置之上增加了一個參數。
KeyPrefixRegionSplitPolicy.prefix_length
2)部分源碼:
先從父類獲取拆分點,如果設置了 prefixLength > 0,就從父類拆分點中截取需要的前綴作為新的拆分點返回。
3)拆分效果:
KeyPrefixRegionSplitPolicy (SteppingSplitPolicy、、BusyRegionSplitPolicy (HBase-2.x Only))按照 RowKey 的前綴去拆分 Region,但是什麼時候拆分,原 Region 容量的最大值是多少還是需要使用 的方法去計算 。
如果所有數據都只有一兩個前綴,那麼採用默認的策略較好。 如果前綴劃分的比較細,查詢就比較容易發生跨 Region 查詢的情況,此時採用KeyPrefixRegionSplitPolicy 較好。
所以這個策略適用的場景是:
4)創建表時配置:
繼承自 ,也是根據 RowKey 前綴來進行拆分的。不同就是:KeyPrefixRegionSplitPolicy 是根據 RowKey 的固定前幾位字元來進行判斷,而 是根據分隔符來判斷的。
1)相關參數:
在 的配置之上增加了一個參數。
.delimiter
2)部分源碼:
先找到分隔符下標位置,然後從父類的拆分點截取出來。
3)拆分效果:
根據 RowKey 中指定分隔字元做為拆分,顯得更加靈活,如 RowKey 的值為「userid_eventtype_eventid」,userId 不是定長的,則 可以取 RowKey 值中從左往右且第一個分隔字元串之前的字元做為拆分串,在該示例中就是「userid」。
4)創建表時配置:
之前的策略都未考慮 Region 熱點問題,考慮某些 Region 可能被頻繁訪問,負荷很大,BusyRegionSplitPolicy 策略同樣繼承自 ,但主要針對 Region 問題,是在 2.x 中新增加的拆分策略。
1)相關參數:
在 的配置之上增加了如下參數:
hbase.busy.policy.blockedRequests
hbase.busy.policy.minAge
hbase.busy.policy.aggWindow
2)部分源碼:
在判斷是否需要進行拆分的時候,先調用父類的 shouldSplit 方法檢驗,如果需要則直接返回 true,否則需要判斷當前時間是否比開始時間大於 minAge 值,如果是的,則計算請求阻塞率 blockedReqRate,如果阻塞率大於設定的閾值,則進行拆分。
阻塞率的計算如下:
主要的計算邏輯是:請求的被阻塞率(aggBlockedRate) = curTime - prevTime 時間內新增的阻塞請求 / 這段時間的總請求。
3)拆分效果:
如果系統常常會出現熱點 Region,又對性能有很高的追求,那麼這種策略可能會比較適合。
它會通過拆分熱點 Region 來緩解熱點 Region 的壓力,但是根據熱點來拆分Region 也會帶來很多不確定性因素,因為不能確定下一個被拆分的 Region 是哪個。
4)創建表時配置:
DisabledRegionSplitPolicy 就是不使用 Region 拆分策略,將所有的數據都寫到同一個 Region 中。
1)全部源碼:
源碼很簡單,就是直接返回 false。
2)拆分效果:
這個策略極少使用。
即使在建表的時候合理的進行了預拆分,還沒有寫入的數據的時候就已經手動分好了 Region,但是隨著數據的持續寫入,我預先分好的 Region 的大小也會達到閾值,那時候還是要依靠 HBase 的自動拆分策略去拆分 Region。
但這種策略也有它的用途:
假如有一批靜態數據,一次存入以後不會再加入新數據,且這批數據主要是用於查詢,為了性能好一些,可以先進行預分區後,各個 Region 數據量相差不多,然後設置拆分策略為禁止拆分,最後導入數據即可。
3)創建表時配置:
已經有自動分區了,為什麼還需要預分區?
HBase 在創建表的時候,會自動為表分配一個Region,當一個 Region 達到拆分條件時(shouldSplit 為 true),HBase 中該 Region 將會進行 split,分裂為2個 Region,以此類推。表在進行 split 的時候,會耗費很多的資源,有大量的 io 操作,頻繁的分區對 HBase 的性能有很大的影響。
所以,HBase 提供了預分區功能,讓用戶可以在創建表的時候對表按照一定的規則分區。
假設初始 10 個 Region,那麼導入大量數據的時候,就會均衡到 10 個 Region 裡面,顯然比初始 1 個 Region 要好很多, 合理的預分區可以減少 Region 熱點問題,提升寫數據的性能和速度,而且也能減少後續的 split 操作 。
首先要明白數據的 RowKey 是如何分布的,然後根據 RowKey 的特點規劃要分成多少 Region,每個 Region 的 startKey 和 endKey 是多少,接著就可以預分區了。
比如,RowKey 的前幾位字元串都是從 0001~0010 的數字,這樣可以分成10個Region:
第一行為第一個 Region 的 stopKey。為什麼後面會跟著一個"|",是因為在ASCII碼中,"|"的值是124,大於所有的數字和字母等符號。
shell中建分區表
也可以通過指定 SPLITS_FILE 的值指定分區文件,從文件中讀取分區值,文件格式如上述例子所示:
預分區後,可以從 HBase ui 頁面觀察到:
HBase API 建預分區表
為防止熱點問題,同時避免 Region Split 後,部分 Region 不再寫數據或者很少寫數據。也為了得到更好的並行性,希望有好的 load blance,讓每個節點提供的請求處理都是均等的,並且 Region 不要經常 split,因為 split 會使 server 有一段時間的停頓,隨機散列加上預分區是比較好的解決方式。
預分區一開始就預建好了一部分 Region,這些 Region 都維護著自已的 start-end keys,再配合上隨機散列,寫數據能均等地命中這些預建的 Region,就能通過良好的負載,提升並行,大大地提高了性能。
hash + 預分區
在 RowKey 的前面拼接通過 hash 生成的隨機字元串,可以生成范圍比較隨機的 RowKey,可以比較均衡分散到不同的 Region 中,那麼就可以解決寫熱點問題。
假設 RowKey 原本是自增長的 long 型,可以將 RowKey 先進行 hash,加上本身 id ,組成rowkey,這樣就生成比較隨機的 RowKey 。
那麼對於這種方式的 RowKey 設計,如何去進行預分區?
partition + 預分區
partition 顧名思義就是分區式,這種分區有點類似於 maprece 中的 partitioner,將區域用長整數作為分區號,每個 Region 管理著相應的區域數據,在 RowKey 生成時,將 id 取模後,然後拼上 id 整體作為 RowKey 。
1. HBase Region 自動拆分策略
2. hbase預分區