深入理解HBase Memstore

原文出處:http://shitouer.cn/2013/02/configuring-hbase-memstore-what-you-should-know/


MemStore是HBase非常重要的組成部分,深入理解MemStore的運行機制、工作原理、相關配置,對HBase集羣管理以及性能調優有非常重要的幫助。

HBase Memstore

首先通過簡單介紹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還有一些其他的好處,例如:

  • 作爲一個內存級緩存,緩存最近增加數據。一種顯而易見的場合是,新插入數據總是比老數據頻繁使用。
  • 在持久化寫入之前,在內存中對Rows/Cells可以做某些優化。比如,當數據的version被設爲1的時候,對於某些CF的一些數據,Memstore緩存了數個對該Cell的更新,在寫入HFile的時候,僅需要保存一個最新的版本就好了,其他的都可以直接拋棄。

有一點需要特別注意:每一次Memstore的flush,會爲每一個CF創建一個新的HFile。 在讀方面相對來說就會簡單一些:HBase首先檢查請求的數據是否在Memstore,不在的話就到HFile中查找,最終返回merged的一個結果給用戶。

HBase Memstore關注要點

迫於以下幾個原因,HBase用戶或者管理員需要關注Memstore並且要熟悉它是如何被使用的:

  • Memstore有許多配置可以調整以取得好的性能和避免一些問題。HBase不會根據用戶自己的使用模式來調整這些配置,你需要自己來調整。
  • 頻繁的Memstore flush會嚴重影響HBase集羣讀性能,並有可能帶來一些額外的負載。
  • Memstore flush的方式有可能影響你的HBase schema設計

接下來詳細討論一下這些要點:

Configuring Memstore Flushes

對Memstore Flush來說,主要有兩組配置項:

  • 決定Flush觸發時機
  • 決定Flush何時觸發並且在Flush時候更新被阻斷(block)

第一組是關於觸發“普通”flush,這類flush發生時,並不影響並行的寫請求。該類型flush的配置項有:

  • hbase.hregion.memstore.flush.size
?
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>
  • base.regionserver.global.memstore.lowerLimit
?
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配置項有:

  • hbase.regionserver.global.memstore.upperLimit
?
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>
  •  hbase.hregion.memstore.block.multiplier
?
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不能持續增長。

頻繁的Memstore Flushes

要避免“寫阻塞”,貌似讓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”,我們幾乎很少有超過它。

Multiple Column Families & Memstore Flush

每次Memstore Flush,會爲每個CF都創建一個新的HFile。這樣,不同CF中數據量的不均衡將會導致產生過多HFile:當其中一個CF的Memstore達到閾值flush時,所有其他CF的也會被flush。如上所述,太頻繁的flush以及過多的HFile將會影響集羣性能。

提示很多情況下,一個CF是最好的設計

HLog (WAL) Size & Memstore Flush

第一張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中再保存這些修改。有兩個屬性可以配置:

  •  hbase.regionserver.hlog.blocksize
  • hbase.regionserver.maxlogs

你可能已經發現,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.

Compression & Memstore Flush

HBase建議壓縮存儲在HDFS上的數據(比如HFiles)。除了節省硬盤空間,同樣也會顯著地減少硬盤和網絡IO。使用壓縮,當Memstore flush並將數據寫入HDFS時候,數據會被壓縮。壓縮不會減慢多少flush的處理過程,卻會大大減少以上所述問題,例如因爲Memstore變大(超過 upper limit)而引起的“寫阻塞”等等。

提示:壓縮庫建議使用Snappy。有關Snappy的介紹及安裝,可分別參考:《Hadoop壓縮-SNAPPY算法》和《Hadoop HBase 配置 安裝 Snappy 終極教程

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