首先經過簡單介紹HBase的讀寫過程來理解一下MemStore究竟是什麼,在何處發揮做用,如何使用到以及爲何要用MemStore。算法
圖一:Memstore Usage in HBase Read/Write Paths緩存
當RegionServer(RS)收到寫請求的時候(write request),RS會將請求轉至相應的Region。每個Region都存儲着一些列(a set of rows)。根據其列族的不一樣,將這些列數據存儲在相應的列族中(Column Family,簡寫CF)。不一樣的CFs中的數據存儲在各自的HStore中,HStore由一個Memstore及一系列HFile組成。Memstore位於RS的主內存中,而HFiles被寫入到HDFS中。當RS處理寫請求的時候,數據首先寫入到Memstore,而後當到達必定的閥值的時候,Memstore中的數據會被刷到HFile中。安全
用到Memstore最主要的緣由是:存儲在HDFS上的數據須要按照row key 排序。而HDFS自己被設計爲順序讀寫(sequential reads/writes),不容許修改。這樣的話,HBase就不可以高效的寫數據,由於要寫入到HBase的數據不會被排序,這也就意味着沒有爲未來的檢索優化。爲了解決這個問題,HBase將最近接收到的數據緩存在內存中(in Memstore),在持久化到HDFS以前完成排序,而後再快速的順序寫入HDFS。須要注意的一點是實際的HFile中,不只僅只是簡單地排序的列數據的列表,詳見Apache HBase I/O – HFile。網絡
除了解決「無序」問題外,Memstore還有一些其餘的好處,例如:app
有一點須要特別注意:每一次Memstore的flush,會爲每個CF建立一個新的HFile。 在讀方面相對來講就會簡單一些:HBase首先檢查請求的數據是否在Memstore,不在的話就到HFile中查找,最終返回merged的一個結果給用戶。oop
迫於如下幾個緣由,HBase用戶或者管理員須要關注Memstore而且要熟悉它是如何被使用的:性能
接下來詳細討論一下這些要點:優化
對Memstore Flush來講,主要有兩組配置項:this
第一組是關於觸發「普通」flush,這類flush發生時,並不影響並行的寫請求。該類型flush的配置項有:
1
2
3
4
5
6
7
8
9
|
<
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
>
|
1
2
3
4
5
6
7
8
9
10
|
<
property
>
<
name
>hbase.regionserver.global.memstore.lowerLimit</
name
>
<
value
>0.35</
value
>
<
description
>Maximum size of all memstores in a region server before
flushes are forced. Defaults to 35% of heap.
This value equal to hbase.regionserver.global.memstore.upperLimit causes
the minimum possible flushing to occur when updates are blocked due to
memstore limiting.
</
description
>
</
property
>
|
須要注意的是第一個設置是每一個Memstore的大小,當你設置該配置項時,你須要考慮一下每臺RS承載的region總量。可能一開始你設置的該值比較小,後來隨着region增多,那麼就有可能由於第二個設置緣由Memstore的flush觸發會變早許多。
第二組設置主要是出於安全考慮:有時候集羣的「寫負載」很是高,寫入量一直超過flush的量,這時,咱們就但願memstore不要超過必定的安全設置。在這種狀況下,寫操做就要被阻止(blocked)一直到memstore恢復到一個「可管理」(manageable)的大小。該類型flush配置項有:
1
2
3
4
5
6
7
8
9
|
<
property
>
<
name
>hbase.regionserver.global.memstore.upperLimit</
name
>
<
value
>0.4</
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.
Updates are blocked and flushes are forced until size of all memstores
in a region server hits hbase.regionserver.global.memstore.lowerLimit.
</
description
>
</
property
>
|
1
2
3
4
5
6
7
8
9
10
11
12
|
<
property
>
<
name
>hbase.hregion.memstore.block.multiplier</
name
>
<
value
>2</
value
>
<
description
>
Block updates if memstore has hbase.hregion.block.memstore
time hbase.hregion.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
>
|
某個節點「寫阻塞」對該節點來講影響很大,可是對於整個集羣的影響更大。HBase設計爲:每一個Region僅屬於一個RS可是「寫負載」是均勻分佈於整個集羣(全部Region上)。有一個如此「慢」的節點,將會使得整個集羣都會變慢(最明顯的是反映在速度上)。
提示:嚴重關切Memstore的大小和Memstore Flush Queue的大小。理想狀況下,Memstore的大小不該該達到hbase.regionserver.global.memstore.upperLimit的設置,Memstore Flush Queue 的size不能持續增加。
要避免「寫阻塞」,貌似讓Flush操做盡可能的早於達到觸發「寫操做」的閾值爲宜。可是,這將致使頻繁的Flush操做,而由此帶來的後果即是讀性能降低以及額外的負載。
每次的Memstore Flush都會爲每一個CF建立一個HFile。頻繁的Flush就會建立大量的HFile。這樣HBase在檢索的時候,就不得不讀取大量的HFile,讀性能會受很大影響。
爲預防打開過多HFile及避免讀性能惡化,HBase有專門的HFile合併處理(HFile Compaction Process)。HBase會週期性的合併數個小HFile爲一個大的HFile。明顯的,有Memstore Flush產生的HFile越多,集羣系統就要作更多的合併操做(額外負載)。更糟糕的是:Compaction處理是跟集羣上的其餘請求並行進行的。當HBase不可以跟上Compaction的時候(一樣有閾值設置項),會在RS上出現「寫阻塞」。像上面說到的,這是最最不但願的。
提示:嚴重關切RS上Compaction Queue 的size。要在其引發問題前,阻止其持續增大。
想了解更多HFile 建立和合並,可參看 Visualizing HBase Flushes And Compactions。
理想狀況下,在不超過hbase.regionserver.global.memstore.upperLimit的狀況下,Memstore應該儘量多的使用內存(配置給Memstore部分的,而不是真個Heap的)。下圖展現了一張「較好」的狀況:
「Somewhat」, because we could configure lower limit to be closer to upper, since we barely ever go over it.
說是「較好」,是由於咱們能夠將「Lower limit」配置的更接近於「Upper limit」,咱們幾乎不多有超過它。
每次Memstore Flush,會爲每一個CF都建立一個新的HFile。這樣,不一樣CF中數據量的不均衡將會致使產生過多HFile:當其中一個CF的Memstore達到閾值flush時,全部其餘CF的也會被flush。如上所述,太頻繁的flush以及過多的HFile將會影響集羣性能。
提示:不少狀況下,一個CF是最好的設計。
第一張HBase Read/Write path圖中,你可能已經注意到當數據被寫入時會默認先寫入Write-ahead Log(WAL)。WAL中包含了全部已經寫入Memstore但還未Flush到HFile的更改(edits)。在Memstore中數據尚未持久化,當RegionSever宕掉的時候,可使用WAL恢復數據。
當WAL(在HBase中成爲HLog)變得很大的時候,在恢復的時候就須要很長的時間。所以,對WAL的大小也有一些限制,當達到這些限制的時候,就會觸發Memstore的flush。Memstore flush會使WAL 減小,由於數據持久化以後(寫入到HFile),就沒有必要在WAL中再保存這些修改。有兩個屬性能夠配置:
你可能已經發現,WAL的最大值由hbase.regionserver.maxlogs * hbase.regionserver.hlog.blocksize (2GB by default)決定。一旦達到這個值,Memstore flush就會被觸發。因此,當你增長Memstore的大小以及調整其餘的Memstore的設置項時,你也須要去調整HLog的配置項。不然,WAL的大小限制可能會首先被觸發,於是,你將利用不到其餘專門爲Memstore而設計的優化。拋開這些不說,經過WAL限制來觸發Memstore的flush並不是最佳方式,這樣作可能會會一次flush不少Region,儘管「寫數據」是很好的分佈於整個集羣,進而頗有可能會引起flush「大風暴」。
提示:最好將hbase.regionserver.hlog.blocksize * hbase.regionserver.maxlogs 設置爲稍微大於hbase.regionserver.global.memstore.lowerLimit * HBASE_HEAPSIZE.
HBase建議壓縮存儲在HDFS上的數據(好比HFiles)。除了節省硬盤空間,一樣也會顯著地減小硬盤和網絡IO。使用壓縮,當Memstore flush並將數據寫入HDFS時候,數據會被壓縮。壓縮不會減慢多少flush的處理過程,卻會大大減小以上所述問題,例如由於Memstore變大(超過 upper limit)而引發的「寫阻塞」等等。
提示:壓縮庫建議使用Snappy。有關Snappy的介紹及安裝,可分別參考:《Hadoop壓縮-SNAPPY算法》和《Hadoop HBase 配置 安裝 Snappy 終極教程
》