MemStore 是 HBase 非常重要的組成部分尉咕,MemStore 作為 HBase 的寫緩存恩尾,保存著數(shù)據(jù)的最近一次更新质况,同時是HBase能夠?qū)崿F(xiàn)高性能隨機(jī)讀寫的重要組成威彰。
MemStore
HBase Table 的每個 Column family 維護(hù)一個 MemStore粪薛,當(dāng)滿足一定條件時 MemStore 會執(zhí)行一次 flush纺铭,文件系統(tǒng)中生成新的 HFile寇钉。而每次 Flush 的最小單位是 Region。
MemStore的主要作用:
更新數(shù)據(jù)存儲在 MemStore 中舶赔,使用 LSM(Log-Structured Merge Tree)數(shù)據(jù)結(jié)構(gòu)存儲扫倡,在內(nèi)存內(nèi)進(jìn)行排序整合。即保證寫入數(shù)據(jù)有序(HFile中數(shù)據(jù)都按照RowKey進(jìn)行排序)竟纳,同時可以極大地提升HBase的寫入性能撵溃。
作為內(nèi)存緩存,讀取數(shù)據(jù)時會優(yōu)先檢查 MemStore锥累,根據(jù)局部性原理缘挑,新寫入的數(shù)據(jù)被訪問的概率更大。
在持久化寫入前可以做某些優(yōu)化桶略,例如:保留數(shù)據(jù)的版本設(shè)置為1语淘,持久化只需寫入最新版本诲宇。
如果一個 HRegion 中 MemStore 過多(Column family 設(shè)置過多),每次 flush 的開銷必然會很大惶翻,并且生成大量的 HFile 影響后續(xù)的各項操作姑蓝,因此建議在進(jìn)行表設(shè)計的時候盡量減少 Column family 的個數(shù)。
Flush 時機(jī)
MemStore 無論是對 HBase 的寫入還是讀取性能都至關(guān)重要维贺,其中 flush 操作又是 MemStore 最核心的操作它掂。MemStore 在多種情況下會執(zhí)行一次 Flush 操作:
再次注意,MemStore 的最小 flush 單元是 HRegion 而不是單個 MemStore溯泣。
hbase.hregion.memstore.flush.size
默認(rèn)值:128M
MemStore 級別限制虐秋,當(dāng) Region 中任意一個 MemStore 的大小(壓縮后的大欣佟)達(dá)到了設(shè)定值客给,會觸發(fā) MemStore flush。hbase.hregion.memstore.block.multiplier
默認(rèn)值:2
Region 級別限制肢簿,當(dāng) Region 中所有 MemStore 的大小總和達(dá)到了設(shè)定值(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size靶剑,默認(rèn) 2* 128M = 256M),會觸發(fā) MemStore flush池充。hbase.regionserver.global.memstore.upperLimit
默認(rèn)值:0.4
Region Server 級別限制桩引,當(dāng)一個 Region Server 中所有 MemStore 的大小總和達(dá)到了設(shè)定值(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,默認(rèn) 0.4 * RS堆內(nèi)存大惺湛洹)坑匠,會觸發(fā)全部 MemStore flush。-
hbase.regionserver.global.memstore.lowerLimit
默認(rèn)值:0.38
與 hbase.regionserver.global.memstore.upperLimit 類似卧惜,區(qū)別是:當(dāng)一個 Region Server 中所有 MemStore 的大小總和達(dá)到了設(shè)定值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize厘灼,默認(rèn) 0.38 * RS堆內(nèi)存大小)咽瓷,會觸發(fā)部分 MemStore flush设凹。Flush 順序是按照 Region 的總 MemStore 大小,由大到小執(zhí)行茅姜,先操作 MemStore 最大的 Region闪朱,再操作剩余中最大的 Region,直至總體 MemStore 的內(nèi)存使用量低于設(shè)定值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize)钻洒。
-
hbase.regionserver.maxlogs
默認(rèn)值:32
當(dāng)一個 Region Server 中 HLog 數(shù)量達(dá)到設(shè)定值奋姿,系統(tǒng)會選取最早的一個 HLog 對應(yīng)的一個或多個 Region 進(jìn)行 flush。當(dāng)增加 MemStore 的大小以及調(diào)整其他的 MemStore 的設(shè)置項時航唆,也需要去調(diào)整 HLog 的配置項胀蛮。否則院刁,WAL的大小限制可能會首先被觸發(fā)糯钙。因而,將利用不到其他專門為Memstore而設(shè)計的優(yōu)化。
需要關(guān)注的 HLog 配置是 HLog 文件大小任岸,由參數(shù) hbase.regionserver.hlog.blocksize 設(shè)置(默認(rèn)512M)再榄,HLog 大小達(dá)到上限,或生成一個新的 HLog
通過WAL限制來觸發(fā)Memstore的flush并非最佳方式享潜,這樣做可能會會一次flush很多Region困鸥,盡管“寫數(shù)據(jù)”是很好的分布于整個集群,進(jìn)而很有可能會引發(fā)flush“大風(fēng)暴”剑按。
hbase.regionserver.optionalcacheflushinterval
默認(rèn)值:3600000
HBase 定期刷新 MemStore疾就,默認(rèn)周期為1小時,確保 MemStore 不會長時間沒有持久化艺蝴。為避免所有的 MemStore 在同一時間都進(jìn)行 flush猬腰,定期的 flush 操作有 20000 左右的隨機(jī)延時。手動觸發(fā)
用戶可以通過shell命令一下分別對一個 Table 或者一個 Region 進(jìn)行 flush:
hbase> flush 'TABLENAME'
hbase> flush 'REGIONNAME'其他
執(zhí)行 Compact 和 Split 之前猜敢,會進(jìn)行一次 flush姑荷。
Flush 阻止更新的情況
出現(xiàn)上述(2)的情況,Region 下所有 Memstore 的總大小超過了 MemStore 默認(rèn)大小的倍數(shù)缩擂,該 Region 在 flush 完成前會 block 新的更新請求鼠冕。
出現(xiàn)上述(3)的情況,RegionServer 所有 MemStore 占整個堆的最大比例超過 hbase.regionserver.global.memstore.upperLimit 設(shè)置值胯盯,該 RegionServer 的更新請求會被 block懈费,一直到 MemStore 恢復(fù)閾值一下。
更新被阻塞對單個節(jié)點和整個集群的影響都很大陨闹,需要關(guān)注 MemStore 的大小和 Memstore Flush Queue 的長度楞捂。
Memstore Flush 流程
為了減少 flush 過程對讀寫的影響,HBase 采用了類似于兩階段提交的方式趋厉,將整個 flush 過程分為三個階段:
prepare 階段:遍歷當(dāng)前 Region 中的所有 MemStore寨闹,將 MemStore 中當(dāng)前數(shù)據(jù)集 kvset 做一個快照 snapshot,然后再新建一個新的 kvset君账,后期的所有寫入操作都會寫入新的 kvset 中繁堡。整個 flush 階段讀操作讀 MemStore 的部分,會分別遍歷新的 kvset 和 snapshot乡数。prepare 階段需要加一把 updateLock 對寫請求阻塞椭蹄,結(jié)束之后會釋放該鎖。因為此階段沒有任何費時操作净赴,因此持鎖時間很短绳矩。
flush 階段:遍歷所有 MemStore,將 prepare 階段生成的 snapshot 持久化為臨時文件玖翅,臨時文件會統(tǒng)一放到目錄.tmp下翼馆。這個過程因為涉及到磁盤IO操作割以,因此相對比較耗時。
commit 階段:遍歷所有的 MemStore应媚,將 flush 階段生成的臨時文件移到指定的 Column family 目錄下严沥,生成對應(yīng)的 Storefile(HFile) 和 Reader,把 Storefile 添加到 HStore 的 Storefiles 列表中中姜,最后再清空 prepare 階段生成的 snapshot消玄。
上述 flush 流程可以通過日志信息查看:
/******* prepare 階段 ********/
2018-07-06 18:33:31,329 INFO [MemStoreFlusher.1] regionserver.HRegion: Started memstore flush for [table],,1528539945017.80ab9764ae70fa97b75057c376726653., current region memstore size 21.73 MB, and 1/1 column families' memstores are being flushed.
/******* flush 階段 ********/
2018-07-06 18:33:31,696 INFO [MemStoreFlusher.1] regionserver.DefaultStoreFlusher: Flushed, sequenceid=40056, memsize=21.7 M, hasBloomFilter=true, into tmp file hdfs://ns/hbase/data/default/[table]/80ab9764ae70fa97b75057c376726653/.tmp/f71e7e8c15774da683bdecaf7cf6cb99
/******* commit 階段 ********/
2018-07-06 18:33:31,718 INFO [MemStoreFlusher.1] regionserver.HStore: Added hdfs://ns/hbase/data/default/[table]/80ab9764ae70fa97b75057c376726653/d/f71e7e8c15774da683bdecaf7cf6cb99, entries=119995, sequenceid=40056, filesize=7.3 M
整個 flush 過程可能涉及到 compact 操作和 split 操作,因為過于復(fù)雜丢胚,不做詳細(xì)講解翩瓜。
MemStore 對業(yè)務(wù)的影響
正常情況下,大部分 Memstore Flush 操作都不會對業(yè)務(wù)讀寫產(chǎn)生太大影響携龟,比如:定期刷新 MemStore奥溺、手動觸發(fā)、單個 MemStore flush骨宠、Region 級別的 flush 以及超過 HLog 數(shù)量限制等情況浮定,這幾種場景只會短暫的阻塞對應(yīng) Region 上的寫請求,阻塞時間很短层亿,毫秒級別桦卒。
然而一旦觸發(fā) Region Server 級別的限制導(dǎo)致 flush,就會對用戶請求產(chǎn)生較大的影響匿又。會阻塞所有落在該 RegionServer 上的更新操作方灾,阻塞時間很長,甚至可以達(dá)到分鐘級別碌更。
導(dǎo)致觸發(fā) RegionServer 級別限制的主要因素:
- Region Server 上運行的 Region 總數(shù)
Region 越多裕偿,Region Server 上維護(hù)的 MemStore 就越多。根據(jù)業(yè)務(wù)表讀寫請求量和 RegionServer 可分配內(nèi)存大小痛单,合理設(shè)置表的分區(qū)數(shù)量(預(yù)分區(qū)的情況)嘿棘。
- Region 上的 Store 數(shù)(表的 Column family 數(shù)量)
每個 Column family 會維護(hù)一個 MemStore,每次 MemStore Flush旭绒,會為每個 Column family 都創(chuàng)建一個新的 HFile鸟妙。當(dāng)其中一個CF的 MemStore 達(dá)到閾值 flush 時,所有其他CF的 MemStore 也會被 flush挥吵,因此不同CF中數(shù)據(jù)量的不均衡將會導(dǎo)致產(chǎn)生過多 HFile 和小文件重父,影響集群性能。很多情況下忽匈,一個CF是最好的設(shè)計房午。
頻繁的 MemStore Flush
頻繁的 MemStore Flush 會創(chuàng)建大量的 HFile。在檢索的時候丹允,就不得不讀取大量的 HFile郭厌,讀性能會受很大影響嗦锐。為預(yù)防打開過多 HFile 及避免讀性能惡化(讀放大),HBase 有專門的 HFile 合并處理(HFile Compaction Process)沪曙,根據(jù)一定的策略,合并小文件和刪除過期數(shù)據(jù)萎羔。后續(xù)的文章會有詳細(xì)介紹液走。