1、數據存儲結構
(1)邏輯結構
邏輯上是一張表,有行有列,但是物理上是k-v存儲的。

一個列族包含n個列,在屋里結構上一個列族就是一個文件夾。一個文件夾中包好多個store文件。
rowKey又叫行鍵,它是有序的(字典順序)。
來看下它的數據模型:
(2)物理結構
HBase在屋里上就是種種k-v存儲的,實現在HDFS上隨機寫操作,就是通過timeStamp來進行版本控制實現的。這種存儲方式很好地解決了稀疏性問題,因為空值不會占據存儲空間,不像MySQL那樣NULL值也要值一定的存儲空間。
2、系統架構

架構角色:
3、運行原理
(1)寫數據流程
1)Client 先訪問 zookeeper,獲取 hbase:meta 表位於哪個 Region Server。
說明:寫入數據時,邏輯順序是先寫入到WAL(HLog),再寫入memStore,但實際源碼流程如下:使用事務回滾機制來確保WAL和memStore同步寫入。
1)先獲取鎖(JUC),保證數據不會寫入成功前數據不會被查詢到
2)更新時間戳(數據中沒定義時間戳則使用系統時間),集群中個HRegionServer的系統時間誤差不能超過配置時間,否則集群啟動不成功
3)內存中構建WAL
4)將構建的WAL追加到最后寫入的WAL,此時不會將WAL同步到HDFS中的HLog中
5)將數據寫入到memStore
6)釋放鎖
7)同步WAL到HLog,如果同步失敗,會回滾memStore(通過doRollBackMemstore=false控制)
8)9).........
(2)MemStore刷寫(flush)

第一種觸發條件:(Region級別Flush)
當某個 memstroe 的大小達到了 hbase.hregion.memstore.flush.size(默認值 128M),其所在 region 的所有 memstore 都會刷寫。
如果寫入速度太快超過了刷寫速度,當 memstore 的大小達到了 hbase.hregion.memstore.flush.size * hbase.hregion.memstore.block.multiplier(默認值 4)大小時,會觸發所有memstore 刷寫並阻塞 Region 所有的寫請求,
此時寫數據會出現 RegionTooBusyException 異常。
<!-- 單個region里memstore的緩存大小,超過那么整個HRegion就會flush,默認128M --> <property> <name>hbase.hregion.memstore.flush.size</name> <value>134217728</value> <description> Memstore will be flushed to disk if size of the memstore exceeds this number of bytes. Value is checked by a thread that runs every hbase.server.thread.wakefrequency. </description> </property> <!-- 當一個HRegion上的memstore的大小滿足hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size, 這個HRegion會執行flush操作並阻塞對該HRegion的寫入 --> <property> <name>hbase.hregion.memstore.block.multiplier</name> <value>4</value> <description> Block updates if memstore has hbase.hregion.memstore.block.multiplier times hbase.hregion.memstore.flush.size bytes. Useful preventing runaway memstore during spikes in update traffic. Without an upper-bound, memstore fills such that when it flushes the resultant flush files take a long time to compact or split, or worse, we OOME. </description> </property>
第二種觸發條件:(RegionServer級別Flush)
當 regionServer 中 memstore 的總大小達到 java_heapsize(堆內存)* hbase.regionserver.global.memstore.size(默認值 0.4)* hbase.regionserver.global.memstore.size.lower.limit(默認值 0.95),
region 會按照其所有 memstore 的大小順序(由大到小)依次進行刷寫。直到 regionServer 中所有 memstore 的總大小減小到上述值以下。
<!-- regionServer的全局memstore的大小,超過該大小會觸發flush到磁盤的操作,默認是堆大小的40%,而且regionserver級別的 flush會阻塞客戶端讀寫。如果RegionServer的堆內存設置為2G,則memstore總內存大小為 2 * 0.4 = 0.8G--> <property> <name>hbase.regionserver.global.memstore.size</name> <value></value> <description>Maximum size of all memstores in a region server before new updates are blocked and flushes are forced. Defaults to 40% of heap (0.4). Updates are blocked and flushes are forced until size of all memstores in a region server hits hbase.regionserver.global.memstore.size.lower.limit. The default value in this configuration has been intentionally left emtpy in order to honor the old hbase.regionserver.global.memstore.upperLimit property if present. </description> </property> <!--可以理解為一個安全的設置,有時候集群的“寫負載”非常高,寫入量一直超過flush的量,這時,我們就希望memstore不要超過一定的安全設置。 在這種情況下,寫操作就要被阻塞一直到memstore恢復到一個“可管理”的大小, 這個大小就是默認值是堆大小 * 0.4 * 0.95,也就是當regionserver級別 的flush操作發送后,會阻塞客戶端寫,一直阻塞到整個regionserver級別的memstore的大小為 堆大小 * 0.4 *0.95為止 --> <property> <name>hbase.regionserver.global.memstore.size.lower.limit</name> <value></value> <description>Maximum size of all memstores in a region server before flushes are forced. Defaults to 95% of hbase.regionserver.global.memstore.size (0.95). A 100% value for this value causes the minimum possible flushing to occur when updates are blocked due to memstore limiting. The default value in this configuration has been intentionally left emtpy in order to honor the old hbase.regionserver.global.memstore.lowerLimit property if present. </description> </property>
第三種觸發條件:(RegionServer級別Flush)
定期 hbase.regionserver.optionalcacheflushinterval(默認3600000即一個小時)進行 MemStore 的刷寫,確保 MemStore 不會長時間沒有持久化。為避免所有的 MemStore 在同一時間進行 flush 而導致問題,
定期的 flush 操作會有一定時間的隨機延時。
第四種觸發條件:(Region級別Flush)
第五種觸發條件:
默認值 128M,單個 MemStore 大小超過該閾值就會觸發 Flush。如果當前集群 Flush 比較頻繁,並且內存資源比較充裕,建議適當調整為 256M。調大的副作用可能是造成宕機時需要分裂的 HLog 數量變多,從而延長故障恢復時間。
• hbase.hregion.memstore.block.multiplier
默認值 4,Region 中所有 MemStore 超過單個 MemStore 大小的倍數達到該參數值時,就會阻塞寫請求並強制 Flush。一般不建議調整,但對於寫入過快且內存充裕的場景,為避免寫阻塞,可以適當調整到5~8。
• hbase.regionserver.global.memstore.size
默認值 0.4,RegionServer 中所有 MemStore 大小總和最多占 RegionServer 堆內存的 40%。這是寫緩存的總比例,可以根據實際場景適當調整,且要與 HBase 讀緩存參數 hfile.block.cache.size(默認也是0.4)配合調整。舊版本參數名稱為 hbase.regionserver.global.memstore.upperLimit。
• hbase.regionserver.global.memstore.size.lower.limit
默認值 0.95,表示 RegionServer 中所有 MemStore 大小的低水位是 hbase.regionserver.global.memstore.size 的 95%,超過該比例就會強制 Flush。一般不建議調整。舊版本參數名稱為 hbase.regionserver.global.memstore.lowerLimit。
• hbase.regionserver.optionalcacheflushinterval
默認值 3600000(即 1 小時),HBase 定期 Flush 所有 MemStore 的時間間隔。一般建議調大,比如 10 小時,因為很多場景下 1 小時 Flush 一次會產生很多小文件,一方面導致 Flush 比較頻繁,另一方面導致小文件很多,影響隨機讀性能,因此建議設置較大值。
(3)讀流程
1)Client 先訪問 zookeeper,獲取 hbase:meta 表位於哪個 Region Server。
2)訪問對應的 Region Server,獲取 hbase:meta 表,根據讀請求的 namespace:table/rowkey,查詢出目標數據位於哪個 Region Server 中的哪個 Region 中。並將該 table 的 region 信息以
(4)StoreFile合並(Compaction)
Minor Compaction 是指選取一些小的、相鄰的 HFile 將他們合並成一個更大的 HFile。默認情況下,Minor Compaction 會刪除所合並 HFile 中的 TTL 過期數據,但是不會刪除手動刪除(也就是 Delete 標記作用的數據)不會被刪除。
概括的說,HBase 會在三種情況下檢查是否要觸發 Compaction,分別是 MemStore Flush、后台線程周期性檢查、手動觸發。
- MemStore Flush:可以說 Compaction 的根源就在於Flush,MemStore 達到一定閾值或觸發條件就會執行 Flush 操作,在磁盤上生成 HFile 文件,正是因為 HFile 文件越來越多才需要 Compact。HBase 每次Flush 之后,都會判斷是否要進行 Compaction,一旦滿足 Minor Compaction 或 Major Compaction 的條件便會觸發執行。
- 后台線程周期性檢查: 后台線程 CompactionChecker 會定期檢查是否需要執行 Compaction,檢查周期為 hbase.server.thread.wakefrequency * hbase.server.compactchecker.interval.multiplier,這里主要考慮的是一段時間內沒有寫入仍然需要做 Compact 檢查。其中參數 hbase.server.thread.wakefrequency 默認值 10000 即 10s,是 HBase 服務端線程喚醒時間間隔,用於 LogRoller、MemStoreFlusher 等的周期性檢查;參數 hbase.server.compactchecker.interval.multiplier 默認值1000,是 Compaction 操作周期性檢查乘數因子,10 * 1000 s 時間上約等於2hrs, 46mins, 40sec。
- 手動觸發:通過 HBase Shell、Master UI 界面或 HBase API 等任一種方式執行 compact、major_compact等命令,會立即觸發 Compaction。
• hbase.hstore.compaction.min
默認值 3,一個 Store 中 HFile 文件數量超過該閾值就會觸發一次 Compaction(Minor Compaction),這里稱該參數為 minFilesToCompact。一般不建議調小,重寫場景下可以調大該參數,比如 5~10 之間,
注意相應調整下一個參數。老版本參數名稱為 hbase.hstore.compactionthreshold。
• hbase.hstore.compaction.max
默認值 10,一次 Minor Compaction 最多合並的 HFile 文件數量,這里稱該參數為 maxFilesToCompact。這個參數也控制着一次壓縮的耗時。一般不建議調整,但如果上一個參數調整了,該參數也應該相應調整,
一般設為 minFilesToCompact 的 2~3 倍。
• hbase.regionserver.thread.compaction.throttle
HBase RegionServer 內部設計了兩個線程池 large compactions 與 small compactions,用來分離處理 Compaction 操作,該參數就是控制一個 Compaction 交由哪一個線程池處理,
默認值是 2 * maxFilesToCompact * hbase.hregion.memstore.flush.size(默認2*10*128M=2560M即2.5G),建議不調整或稍微調大。
• hbase.regionserver.thread.compaction.large/small
默認值 1,表示 large compactions 與 small compactions 線程池的大小。一般建議調整到 2~5,不建議再調太大比如10,否則可能會消費過多的服務端資源造成不良影響。
• hbase.hstore.blockingStoreFiles
<!-- 每個store阻塞更新請求的閥值,表示如果當前hstore中文件數大於該值,系統將會強制執行compaction操作進行文件合並, 合並的過程會阻塞整個hstore的寫入,這樣有個好處是避免compaction操作趕不上Hfile文件的生成速率 --> <property> <name>hbase.hstore.blockingStoreFiles</name> <value>10</value> <description> If more than this number of StoreFiles in any one Store (one StoreFile is written per flush of MemStore) then updates are blocked for this HRegion until a compaction is completed, or until hbase.hstore.blockingWaitTime has been exceeded. </description> </property>
默認值 10,表示一個 Store 中 HFile 文件數量達到該值就會阻塞寫入,等待 Compaction 的完成。一般建議調大點,比如設置為 100,避免出現阻塞更新的情況,阻塞日志如下:
too many store files; delaying flush up to 90000ms
生產環境建議認真根據實際業務量做好集群規模評估,如果小集群遇到了持續寫入過快的場景,合理擴展集群也非常重要。
(5)數據切分(Split)

默認情況下,每個 Table 起初只有一個 Region,隨着數據的不斷寫入,Region 會自動進行拆分。剛拆分時,兩個子 Region 都位於當前的 Region Server,但處於負載均衡的考慮,HMaster 有可能會將某個 Region 轉移給其他的 Region Server。
