HBase 是目前主流的 NoSQL 數(shù)據(jù)庫(kù),是一個(gè)高可靠、高性能、高伸縮的分布式 KV 存儲(chǔ)系統(tǒng),本文講解 HBase 兩個(gè)核心機(jī)制——刷寫(xiě)(Flush)與合并(Compaction),重點(diǎn)介紹其原理及參數(shù)配置建議。
1、為什么要進(jìn)行刷寫(xiě)和合并
HBase 是 Google BigTable 的開(kāi)源實(shí)現(xiàn),底層存儲(chǔ)引擎是基于 LSM 樹(shù)(Log-Structured Merge Tree)數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)的。寫(xiě)入數(shù)據(jù)時(shí)會(huì)先寫(xiě) WAL 日志,再將數(shù)據(jù)寫(xiě)到寫(xiě)緩存 MemStore 中,等寫(xiě)緩存達(dá)到一定規(guī)?;蚱渌|發(fā)條件時(shí)會(huì) Flush 刷寫(xiě)到磁盤(pán),生成一個(gè) HFile 文件,這樣就將磁盤(pán)隨機(jī)寫(xiě)變成了順序?qū)?,提高了?xiě)性能。基本拓?fù)鋱D:
隨著時(shí)間推移,寫(xiě)入的 HFile 會(huì)越來(lái)越多,讀取數(shù)據(jù)時(shí)就會(huì)因?yàn)橐M(jìn)行多次io導(dǎo)致性能降低,因此 HBase 會(huì)定期執(zhí)行 Compaction 操作以合并減少 HFile 數(shù)量,提升讀性能。
2、Flush 觸發(fā)條件和參數(shù)
理解 Flush 的觸發(fā)條件非常重要,從中我們也可以看出何時(shí)會(huì)阻塞寫(xiě)請(qǐng)求,總結(jié)有 7 種情況會(huì)觸發(fā) Flush:
當(dāng)一個(gè) MemStore 大小達(dá)到閾值 hbase.hregion.memstore.flush.size(默認(rèn)128M)時(shí),會(huì)觸發(fā) MemStore 的刷寫(xiě)。這個(gè)時(shí)候不會(huì)阻塞寫(xiě)請(qǐng)求。
當(dāng)一個(gè) Region 中所有 MemStore 總大小達(dá)到 hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size(默認(rèn)4*128M=512M)時(shí),會(huì)觸發(fā) MemStore 的刷寫(xiě),并阻塞 Region 所有的寫(xiě)請(qǐng)求,此時(shí)寫(xiě)數(shù)據(jù)會(huì)出現(xiàn) RegionTooBusyException異常。
當(dāng)一個(gè) RegionServer 中所有 MemStore 總大小達(dá)到 hbase.regionserver.global.memstore.size.lower.limit* hbase.regionserver.global.memstore.size * hbase_heapsize(低水位閾值,默認(rèn)0.95 * 0.4 * RS 堆大?。r(shí),會(huì)觸發(fā) RegionServer 中內(nèi)存占用大的 MemStore 的刷寫(xiě);達(dá)到 hbase.regionserver.global.memstore.size * hbase_heapsize(高水位閾值,默認(rèn)0.4 * RS堆大?。r(shí),不僅會(huì)觸發(fā) Memstore 的刷寫(xiě),還會(huì)阻塞 RegionServer 所有的寫(xiě)請(qǐng)求,直到 Memstore 總大小降到低水位閾值以下。
當(dāng)一個(gè) RegionServer 的 HLog 即WAL文件數(shù)量達(dá)到上限(可通過(guò)參數(shù)hbase.regionserver.maxlogs 配置,默認(rèn)32)時(shí),也會(huì)觸發(fā) MemStore 的刷寫(xiě),HBase 會(huì)找到最舊的 HLog 文件對(duì)應(yīng)的 Region 進(jìn)行刷寫(xiě) 。
當(dāng)一個(gè) Region 的更新次數(shù)達(dá)到 hbase.regionserver.flush.per.changes(默認(rèn)30000000即3千萬(wàn))時(shí),也會(huì)觸發(fā) MemStore 的刷寫(xiě)。
定期 hbase.regionserver.optionalcacheflushinterval(默認(rèn)3600000即一個(gè)小時(shí))進(jìn)行 MemStore 的刷寫(xiě),確保 MemStore 不會(huì)長(zhǎng)時(shí)間沒(méi)有持久化。為避免所有的 MemStore 在同一時(shí)間進(jìn)行 flush 而導(dǎo)致問(wèn)題,定期的 flush 操作會(huì)有一定時(shí)間的隨機(jī)延時(shí)。
手動(dòng)執(zhí)行 flush 操作,我們可以通過(guò) hbase shell 或 API 對(duì)一張表或一個(gè) Region 進(jìn)行 flush。
上面是 Flush 的幾個(gè)觸發(fā)條件,從中我們拿到 5 個(gè)和 Flush 有關(guān)的重要參數(shù),并給出調(diào)整建議:
1、hbase.hregion.memstore.flush.size
默認(rèn)值 128M,單個(gè) MemStore 大小超過(guò)該閾值就會(huì)觸發(fā) Flush。如果當(dāng)前集群 Flush 比較頻繁,并且內(nèi)存資源比較充裕,建議適當(dāng)調(diào)整為 256M。調(diào)大的副作用可能是造成宕機(jī)時(shí)需要分裂的 HLog 數(shù)量變多,從而延長(zhǎng)故障恢復(fù)時(shí)間。
2、hbase.hregion.memstore.block.multiplier
默認(rèn)值 4,Region 中所有 MemStore 超過(guò)單個(gè) MemStore 大小的倍數(shù)達(dá)到該參數(shù)值時(shí),就會(huì)阻塞寫(xiě)請(qǐng)求并強(qiáng)制 Flush。一般不建議調(diào)整,但對(duì)于寫(xiě)入過(guò)快且內(nèi)存充裕的場(chǎng)景,為避免寫(xiě)阻塞,可以適當(dāng)調(diào)整到5~8。
3、hbase.regionserver.global.memstore.size
默認(rèn)值 0.4,RegionServer 中所有 MemStore 大小總和最多占 RegionServer 堆內(nèi)存的 40%。這是寫(xiě)緩存的總比例,可以根據(jù)實(shí)際場(chǎng)景適當(dāng)調(diào)整,且要與 HBase 讀緩存參數(shù) hfile.block.cache.size(默認(rèn)也是0.4)配合調(diào)整。舊版本參數(shù)名稱(chēng)為 hbase.regionserver.global.memstore.upperLimit。
4、hbase.regionserver.global.memstore.size.lower.limit
默認(rèn)值 0.95,表示 RegionServer 中所有 MemStore 大小的低水位是 hbase.regionserver.global.memstore.size 的 95%,超過(guò)該比例就會(huì)強(qiáng)制 Flush。一般不建議調(diào)整。舊版本參數(shù)名稱(chēng)為 hbase.regionserver.global.memstore.lowerLimit。
5、hbase.regionserver.optionalcacheflushinterval
默認(rèn)值 3600000(即 1 小時(shí)),HBase 定期 Flush 所有 MemStore 的時(shí)間間隔。一般建議調(diào)大,比如 10 小時(shí),因?yàn)楹芏鄨?chǎng)景下 1 小時(shí) Flush 一次會(huì)產(chǎn)生很多小文件,一方面導(dǎo)致 Flush 比較頻繁,另一方面導(dǎo)致小文件很多,影響隨機(jī)讀性能,因此建議設(shè)置較大值。
上面就是 Flush 的觸發(fā)條件及核心參數(shù),理解并適當(dāng)調(diào)整參數(shù)有利于維護(hù) HBase 集群的穩(wěn)定性。
3、Compaction 類(lèi)型、觸發(fā)時(shí)機(jī)和參數(shù)
從上面分析我們知道,HBase 會(huì)定期執(zhí)行 Compaction 合并 HFile,提升讀性能,其實(shí)就是以短時(shí)間內(nèi)的io消耗,換取相對(duì)穩(wěn)定的讀取性能。
Compaction 類(lèi)型
Compaction 分為兩種:Minor Compaction 與 Major Compaction,可以稱(chēng)為小合并、大合并,簡(jiǎn)單示意圖:
Minor Compaction 是指選取一些小的、相鄰的 HFile 將他們合并成一個(gè)更大的 HFile。默認(rèn)情況下,Minor Compaction 會(huì)刪除選取 HFile 中的 TTL 過(guò)期數(shù)據(jù)。
Major Compaction 是指將一個(gè) Store 中所有的 HFile 合并成一個(gè) HFile,這個(gè)過(guò)程會(huì)清理三類(lèi)沒(méi)有意義的數(shù)據(jù):被刪除的數(shù)據(jù)(打了 Delete 標(biāo)記的數(shù)據(jù))、TTL 過(guò)期數(shù)據(jù)、版本號(hào)超過(guò)設(shè)定版本號(hào)的數(shù)據(jù)。另外,一般情況下,Major Compaction 時(shí)間會(huì)持續(xù)比較長(zhǎng),整個(gè)過(guò)程會(huì)消耗大量系統(tǒng)資源,對(duì)上層業(yè)務(wù)有比較大的影響。因此,生產(chǎn)環(huán)境下通常關(guān)閉自動(dòng)觸發(fā) Major Compaction 功能,改為手動(dòng)在業(yè)務(wù)低峰期觸發(fā)。
Compaction 觸發(fā)時(shí)機(jī)
概括的說(shuō),HBase 會(huì)在三種情況下檢查是否要觸發(fā) Compaction,分別是 MemStore Flush、后臺(tái)線(xiàn)程周期性檢查、手動(dòng)觸發(fā)。
MemStore Flush:可以說(shuō) Compaction 的根源就在于Flush,MemStore 達(dá)到一定閾值或觸發(fā)條件就會(huì)執(zhí)行 Flush 操作,在磁盤(pán)上生成 HFile 文件,正是因?yàn)?HFile 文件越來(lái)越多才需要 Compact。HBase 每次Flush 之后,都會(huì)判斷是否要進(jìn)行 Compaction,一旦滿(mǎn)足 Minor Compaction 或 Major Compaction 的條件便會(huì)觸發(fā)執(zhí)行。
后臺(tái)線(xiàn)程周期性檢查: 后臺(tái)線(xiàn)程 CompactionChecker 會(huì)定期檢查是否需要執(zhí)行 Compaction,檢查周期為 hbase.server.thread.wakefrequency * hbase.server.compactchecker.interval.multiplier,這里主要考慮的是一段時(shí)間內(nèi)沒(méi)有寫(xiě)入仍然需要做 Compact 檢查。其中參數(shù) hbase.server.thread.wakefrequency 默認(rèn)值 10000 即 10s,是 HBase 服務(wù)端線(xiàn)程喚醒時(shí)間間隔,用于 LogRoller、MemStoreFlusher 等的周期性檢查;參數(shù) hbase.server.compactchecker.interval.multiplier 默認(rèn)值1000,是 Compaction 操作周期性檢查乘數(shù)因子,10 * 1000 s 時(shí)間上約等于2hrs, 46mins, 40sec。
手動(dòng)觸發(fā):通過(guò) HBase Shell、Master UI 界面或 HBase API 等任一種方式執(zhí)行 compact、major_compact等命令,會(huì)立即觸發(fā) Compaction。
Compaction 核心參數(shù)
和上面類(lèi)似,這里總結(jié)了幾個(gè)和 Compaction 有關(guān)的重要參數(shù),并給出調(diào)整建議:
1、hbase.hstore.compaction.min
默認(rèn)值 3,一個(gè) Store 中 HFile 文件數(shù)量超過(guò)該閾值就會(huì)觸發(fā)一次 Compaction(Minor Compaction),這里稱(chēng)該參數(shù)為 minFilesToCompact。一般不建議調(diào)小,重寫(xiě)場(chǎng)景下可以調(diào)大該參數(shù),比如 5~10 之間,注意相應(yīng)調(diào)整下一個(gè)參數(shù)。老版本參數(shù)名稱(chēng)為 hbase.hstore.compactionthreshold。
2、hbase.hstore.compaction.max
默認(rèn)值 10,一次 Minor Compaction 最多合并的 HFile 文件數(shù)量,這里稱(chēng)該參數(shù)為 maxFilesToCompact。這個(gè)參數(shù)也控制著一次壓縮的耗時(shí)。一般不建議調(diào)整,但如果上一個(gè)參數(shù)調(diào)整了,該參數(shù)也應(yīng)該相應(yīng)調(diào)整,一般設(shè)為 minFilesToCompact 的 2~3 倍。
3、hbase.regionserver.thread.compaction.throttle
HBase RegionServer 內(nèi)部設(shè)計(jì)了兩個(gè)線(xiàn)程池 large compactions 與 small compactions,用來(lái)分離處理 Compaction 操作,該參數(shù)就是控制一個(gè) Compaction 交由哪一個(gè)線(xiàn)程池處理,默認(rèn)值是 2 * maxFilesToCompact * hbase.hregion.memstore.flush.size(默認(rèn)210128M=2560M即2.5G),建議不調(diào)整或稍微調(diào)大。
4、hbase.regionserver.thread.compaction.large/small
默認(rèn)值 1,表示 large compactions 與 small compactions 線(xiàn)程池的大小。一般建議調(diào)整到 2~5,不建議再調(diào)太大比如10,否則可能會(huì)消費(fèi)過(guò)多的服務(wù)端資源造成不良影響。
5、hbase.hstore.blockingStoreFiles
默認(rèn)值 10,表示一個(gè) Store 中 HFile 文件數(shù)量達(dá)到該值就會(huì)阻塞寫(xiě)入,等待 Compaction 的完成。一般建議調(diào)大點(diǎn),比如設(shè)置為 100,避免出現(xiàn)阻塞更新的情況,阻塞日志如下:
too many store files; delaying flush up to 90000ms
生產(chǎn)環(huán)境建議認(rèn)真根據(jù)實(shí)際業(yè)務(wù)量做好集群規(guī)模評(píng)估,如果小集群遇到了持續(xù)寫(xiě)入過(guò)快的場(chǎng)景,合理擴(kuò)展集群也非常重要。
6、hbase.hregion.majorcompaction
默認(rèn)值 604800000 ms 即7天,這是 Major Compaction 周期性觸發(fā)執(zhí)行的時(shí)間間隔。通常 Major Compaction 持續(xù)時(shí)間較長(zhǎng)、資源消耗較大,一般設(shè)為 0,表示關(guān)閉自動(dòng)觸發(fā),建議在業(yè)務(wù)低峰期時(shí)手動(dòng)執(zhí)行。
4、總結(jié)
本文概括的介紹了 HBase 的刷寫(xiě)和合并機(jī)制,主要是 Flush 的觸發(fā)條件與核心參數(shù)、Compaction 的觸發(fā)時(shí)機(jī)與核心參數(shù)等,并都給出了具體參數(shù)的調(diào)整建議,希望能給 HBase 使用者提供有價(jià)值的參考。
往期文章精選:
1、如何快速全面掌握Kafka?5000字吐血整理
2、一文讀懂 HBase 核心原理與應(yīng)用場(chǎng)景
3、京東JDHBase異地多活實(shí)踐
4、美團(tuán)點(diǎn)評(píng)基于 Flink 的實(shí)時(shí)數(shù)倉(cāng)平臺(tái)實(shí)踐
如果您喜歡這篇文章,點(diǎn)【在看】與轉(zhuǎn)發(fā)都是一種鼓勵(lì),期待得到您的認(rèn)可 ?(^_-)