消滅毛刺!HBase2.0全鏈路offheap效果拔群
阿里雲HBase2.0版本正式上線
阿里雲HBase2.0版本是基於社群2018年釋出的HBase2.0.0版本開發的全新版本。在社群HBase2.0.0版本基礎上,做了大量的改進和優化,吸收了眾多阿里內部成功經驗,比社群HBase版本具有更好的穩定性和效能,同時具備了HBase2.0提供的全新能力。HBase2.0提供的新功能介紹可以參照這篇文章。如果想要申請使用全新的HBase2.0版本,可以在此連結申請試用。在HBase2.0提供的眾多功能中,最引人注目的就是全鏈路的offheap能力了。根據HBase社群官方文件的說法,全鏈路的offheap功能能夠顯著減少JVM heap裡的資料生成和拷貝,減少垃圾的產生,減少GC的停頓時間。
線上業務在使用hbase讀寫資料時,我們可能會發現,HBase的平均延遲會很低,可能會低於1ms,但P999延遲(99.9%請求返回的最大時間)可能會高達數百ms。這就是所謂的”毛刺”,這些毛刺可能會造成我們的線上業務出現部分請求超時,造成服務質量的下降。而對於HBase來說,GC的停頓,很多時候是造成這樣的毛刺的“罪非禍首”。那HBase2.0中的全鏈路offheap對減少GC停頓,降低P999延遲,真的有那麼神奇的功效嗎?
全鏈路offheap原理
在HBase的讀和寫鏈路中,均會產生大量的記憶體垃圾和碎片。比如說寫請求時需要從Connection的ByteBuffer中拷貝資料到KeyValue結構中,在把這些KeyValue結構寫入memstore時,又需要將其拷貝到MSLAB中,WAL Edit的構建,Memstore的flush等等,都會產生大量的臨時物件,和生命週期結束的物件。隨著寫壓力的上升,GC的壓力也會越大。讀鏈路也同樣存在這樣的問題,cache的置換,block資料的decoding,寫網路中的拷貝等等過程,都會無形中加重GC的負擔。而HBase2.0中引入的全鏈路offheap功能,正是為了解決這些GC問題。大家知道Java的記憶體分為onheap和offheap,而GC只會整理onheap的堆。全鏈路Offheap,就意味著HBase在讀寫過程中,KeyValue的整個生命週期都會在offheap中進行,HBase自行管理offheap的記憶體,減少GC壓力和GC停頓。
寫鏈路的offheap包括以下幾個優化:
- 在RPC層直接把網路流上的KeyValue讀入offheap的bytebuffer中
- 使用offheap的MSLAB pool
- 使用支援offheap的Protobuf版本(3.0+)
讀鏈路的offheap主要包括以下幾個優化:
- 對BucketCache引用計數,避免讀取時的拷貝
- 使用ByteBuffer做為服務端KeyValue的實現,從而使KeyValue可以儲存在offheap的記憶體中
- 對BucketCache進行了一系列效能優化
對比測試
全鏈路offheap效果怎麼樣,是騾子是馬,都要拿出來試試了。測試的準備工作和相關引數如下:
HBase版本
本次測試選用的1.x版本是雲HBase1.1版本截止目前為止最新的AliHB-1.4.9版本,2.x版本是雲HBase2.0版本截止目前為止最新的AliHB-2.0.1。這裡所有的版本號均為阿里內部HBase分支——AliHB的版本號,與社群的版本號無任何關聯。
機型
所有的測試都是針對一臺8核16G的ECS機器上部署的RegionServer。底層的HDFS共有兩個datanode(副本數為2),其中一個與該RegionServer部署在同一臺。每個datanode節點掛載了4塊150GB的SSD雲盤
測試工具
本次測試所用的是hbase自帶的pe工具,由於原生的PE工具不支援不支援單行put和指定batch put數量,因此我對PE工具做了一定的改造,並回饋給了社群,具體內容和使用方法參見這篇文章。
表屬性
測試表的分割槽為64個,compression演算法為SNAPPY,Encoding設定為NONE。所有的region都只在一臺RegionServer上。
相關的HBase引數
共同引數
- HBase的heap大小為9828MB,其中新生代區大小為1719MB
- 使用的GC演算法為CMS GC,當老年代佔用大小超過75%時開始CMS GC。
- hfile.block.cache.size 為0.4, 也就是說預設的lru cache的大小為3931.2MB
- hbase.regionserver.global.memstore.size 為0.35, 即預設的memstore的大小為3439.8MB
- 開啟了讀寫分離,在做寫相關的測試時,寫執行緒為90個,讀執行緒為10個。在做讀相關測試時(包括讀寫混合),寫執行緒為20個,讀執行緒為80個
HBase2.xoffheap相關引數
在測寫場景時,使用了HBase2.x的預設引數,即只開啟了RPC鏈路上的offheap,並沒有開始memstore的offheap。因為根據測試,我們發現開啟memstore的offheap並沒有帶來多大改善,究其原因,還是因為Memstore的offheap只是把KeyValue資料offheap,而Memstore本身使用的Java原生的ConcurrentSkipListMap,其索引結構會在JVM的heap中產生大量的記憶體碎片,因此只把KeyValue offheap的效果並不是很明顯。畢竟,在HBase-1.x開始,就有了MSLAB來管理Memstore中的KeyValue物件,記憶體結構已經比較緊湊。
在測讀場景時:
- hbase-env.sh中設定HBASE_OFFHEAPSIZE=5G (RPC和HDFS 客戶端需要部分DirectMemory)
- hbase.bucketcache.ioengine 調成offheap
- hbase.bucketcache.size 調成 3911,即使用3911MB的DirectMemory來做L2 的cache來 cache data block(之前的測試發現L1中meta block index block的大小大約為20MB,所以在原來onheap的cache基礎上減去了20MB)
- 由於cahce的一部分放入offheap,heapsize減至6290MB
- block cache的比例不變,用來做L1 cache來cache META block(可能遠遠大約meta block的需求,但測試中只需保證meta block 100%命中即可,大了不會影響測試)
注意,本次測試旨在測試HBase2.x與HBase1.x版本在相同壓力下延遲和GC的表現情況,並非測試HBase的最大吞吐能力,因此測試所用的客戶端執行緒數也只限制在了60~64個,遠沒有達到雲HBase的最大吞吐能力
單行寫場景
單行寫測試時使用PE工具開啟64個寫執行緒,每個寫執行緒隨機往HBase表中寫入150000行,共960w行。每行的value size為200bytes。所用的PE命令為
hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=150000 --autoFlush=true --presplit=64 randomWrite 64
版本 | TPS | AVG RT | 95% RT | 99% RT | 99.9% RT | MAX RT |
---|---|---|---|---|---|---|
AliHB-1.4.9 | 38737 | 1.1ms | 2ms | 2ms | 45ms | 140ms |
AliHB-2.0.1 | 40371 | 0.7ms | 1ms | 2ms | 5ms | 140ms |
版本 | AVG younggc Time | younggc GC頻率 |
---|---|---|
AliHB-1.4.9 | 90ms | 0.6次/s |
AliHB-2.0.1 | 110ms | 0.28次/s |
可以看到,使用了HBase-2.x的寫鏈路offheap後,單行寫的P999延遲從45ms降低到了5ms,效果非常明顯。同時吞吐有5%的提升,帶來這種效果的原因就是寫鏈路的offheap使HBase在heap的young區減少了臨時物件的產生,younggc發生的頻率從0.6次每秒降低到了0.28次每秒。這樣受到younggc影響的請求量也會大大減少。因此P999延遲急劇下降.
批量寫
在批量寫測試中,一次batch的個數是100。使用的命令為:
hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=200000 --autoFlush=true --presplit=64 --multiPut=100 randomWrite 64
測試的場景和引數配置與單行寫保持一致
版本 | TPS | AVG RT | 95% RT | 99% RT | 99.9% RT | MAX RT |
---|---|---|---|---|---|---|
AliHB-1.4.9 | 81477 | 72ms | 110ms | 220ms | 350ms | 420ms |
AliHB-2.0.1 | 97985 | 67ms | 75ms | 220ms | 280ms | 300ms |
版本 | AVG younggc Time | younggc GC頻率 |
---|---|---|
AliHB-1.4.9 | 120ms | 0.6次/s |
AliHB-2.0.1 | 180ms | 0.28次/s |
可以看到,使用了HBase-2.x的寫鏈路offheap後,從平均延遲到最大延遲,都有不同程度的下降,GC的頻率也降到1.x版本的一半以下。因此吞吐也上漲了20%。
100%Cache命中單行Get
在此場景中,先使用以下命令先往表中灌了120w行資料
hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=200000 --autoFlush=true --presplit=64 --multiPut=100 sequentialWrite 60
再保證所有資料刷盤,major compact成一個檔案後,先做cache的預熱,然後使用如下命令進行單行讀取:
hbase pe --nomapred --oneCon=true --rows=200000 randomRead 60
測試結果如下:
版本 | QPS | AVG RT | 95% RT | 99% RT | 99.9% RT | MAX RT | |
---|---|---|---|---|---|---|---|
AliHB-1.4.9 | 53895 | 0.04ms | 1ms | 1ms | 1ms | 30ms | |
AliHB-2.0.1 | 49518 | 0.05ms | 0ms | 1ms | 1ms | 14ms |
注:百分比的延遲統計最低解析度是1ms,所以低於1ms時會顯示為0
版本 | AVG younggc Time | younggc GC頻率 |
---|---|---|
AliHB-1.4.9 | 25ms | 0.4次/s |
AliHB-2.0.1 | 8ms | 0.35次/s |
可以看到,在100%記憶體命中場景下,HBase2.x的吞吐效能有了8%的下滑。這是預料之中的,這在HBase的官方文件中也有解釋:讀取offheap的記憶體會比讀onheap的記憶體效能會稍稍下滑。另外,由於在100%記憶體命中的場景下,onheap的cache也不會發生置換,所以產生的gc開銷會比較小,所以在這個場景中,HBase1.x版本的P999延遲也已經比較低。但是,在這個GC不會很嚴重的場景裡(沒有寫,沒有開Block-encoding,cache裡內容不用decode可以直接使用),HBase2.x版本仍然可以把最大延遲降到1.x版本的一半,非常難能可貴。
部分cache命中單行讀
在這個場景中,先使用以下命令往表中灌了3600w行資料,這些資料會超過設定的cache大小,從而會產生一定的cache miss。
灌資料:
hbase pe --nomapred --oneCon=true --valueSize=200 --compress=SNAPPY --rows=600000 --autoFlush=true --presplit=64 --multiPut=100 sequentialWrite 60
再保證所有資料刷盤,major compact成一個檔案後,先做cache的預熱,然後使用如下命令進行單行讀取:
hbase pe --nomapred --oneCon=true --rows=600000 randomRead 60
版本 | QPS | AVG RT | 95% RT | 99% RT | 99.9% RT | MAX RT |
---|---|---|---|---|---|---|
AliHB-1.4.9 | 14944 | 2.5ms | 5ms | 80ms | 200ms | 300ms |
AliHB-2.0.1 | 15372 | 1.7ms | 5ms | 34ms | 65ms | 130ms |
版本 | AVG younggc Time | younggc GC頻率 | CMS GC Remark AVG Time | CMS GC 頻率 |
---|---|---|---|---|
AliHB-1.4.9 | 80ms | 2.4次/s | 50ms | 0.25次/s |
AliHB-2.0.1 | 21ms | 2.5次/s | 0 | 0 |
在部分cache命中的場景中,由於會有一定的cahce miss,在讀的過程中,會產生cache內容的置換。如果這些記憶體的置換髮生在heap裡,會顯著加重GC的負擔。因此,在這個GC壓力比較大的場景中,HBase2.x的全鏈路讀offheap產生了非常優秀的效果,無論是吞吐,平均延遲還是P999和最大延遲,都全面超越HBase1.x版本。由於cache不會在heap中產生垃圾,因此GC的頻率和耗時都顯著降低,基本消滅了CMSGC。更加難能可貴的是,使用了offheap的bucketcache由於每個bucket都是固定大小,因此在放入不定大小的data block時不可能完全放滿,從而會造成一些空間的浪費。因此雖然我把兩者的cache大小調到一樣的大小,HBase1.x的測試中,data block的命中率有58%,HBase2.x的測試中命中率只有40%。也就是說,HBase2.x在命中率更低的情況下,取得的吞吐和延遲都更加優秀!但這從另外一個方面說明,同樣的記憶體大小,在使用offheap功能後,cache的命中率會降低,因此使用offheap時最好使用速度更高的介質做儲存,比如本次測試中選用的SSD雲盤。保證讀取速度不會被落盤而拖慢太多。
讀寫混合測試
讀寫混合測試是大部分生產環境中面對的真實場景。大批量的寫和部分命中的讀都會產生GC壓力,兩者一起發生,GC壓力可想而知。
在這個測試中,灌資料和讀取和部分cache命中場景中使用的命令一致。只不過在讀取的同時,在另外一臺客戶端上起了一個20個執行緒的批量寫測試,去寫另外一個Table
hbase pe --nomapred --oneCon=true --valueSize=200 --table=WriteTable --compress=SNAPPY --blockEncoding=DIFF --rows=600000000 --autoFlush=true --presplit=64 --multiPut=100 randomWrite 20
版本 | QPS | AVG RT | 95% RT | 99% RT | 99.9% RT | MAX RT | |
---|---|---|---|---|---|---|---|
AliHB-1.4.9 | 3945 | 11ms | 5ms | 180ms | 8700ms | 9000ms | |
AliHB-2.0.1 | 12028 | 2ms | 5ms | 45ms | 100ms | 250ms | |
注:表中的QPS指的是讀的吞吐
版本 | AVG younggc Time | younggc GC頻率 | CMS GC Remark AVG Time | CMS GC 頻率 | Full GC time(concurrent mode failure) |
---|---|---|---|---|---|
AliHB-1.4.9 | 80ms | 0.7次/s | /(絕大部分退化成full gc) | 0.08次/s | 約7~9s |
AliHB-2.0.1 | 40ms | 2.1次/s | / | 幾乎為0 | 無 |
在讀寫混合測試中,在此壓力下,CMS GC的速度已經跟不上heap中產生的垃圾的速度。因此在發生CMS時,由於CMS還沒完成時old區已經滿(concurrent mode failure),因此CMS GC都退化成了Full GC,從而產生了7到9s的‘stop the world’停頓。因此,1.x中P999被這樣的Full GC影響,P999已經上升到了8700ms。而由於HBase2.x使用了讀鏈路offheap。在此場景中仍然穩如泰山,CMS GC發生的頻率幾乎為0。所以在讀寫混合場景中,HBase2.x的吞吐是HBase1.x的4倍,P999延遲仍然保持在了100ms之內!
總結
通過上面的測試,我們發現HBase2.x的全鏈路offheap功能確實能夠降低GC停頓時間,在各個場景中,都顯示出了非常顯著的效果。特別是在部分cache命中和讀寫混合這兩個通常在生產環境中遇到的場景,可謂是效果拔群。所以說HBase2.x中的全鏈路offheap是我們在生產環境中去降低毛刺,增加吞吐的利器。
雲端使用
HBase2.0版本目前已經在阿里雲提供商業化服務,任何有需求的使用者都可以在阿里雲端使用深入改進的、一站式的HBase服務。雲HBase版本與自建HBase相比在運維、可靠性、效能、穩定性、安全、成本等方面均有很多的改進,歡迎大家通過下面的連線申請使用阿里雲HBase2.0版本,使用全鏈路offheap這個利器去給生產服務帶來更好的穩定性和服務質量。
https://www.aliyun.com/product/hbase
相關文章
- 免費廣告效果監測服務,實現全鏈路營銷效果跟蹤
- 全鏈路壓測(1):認識全鏈路壓測
- 全鏈路壓測(5):生產全鏈路壓測實施全流程
- QML文字灰飛煙滅效果
- 挖礦病毒消滅記二
- Spring的nohttp宣言:消滅http://SpringHTTP
- 前端全鏈路優化前端優化
- 全鏈路壓測(4):全鏈路壓測的價值是什麼?
- 消滅 Java 程式碼的“壞味道”Java
- “敏捷版”全鏈路壓測敏捷
- 全鏈路灰度之 RocketMQ 灰度MQ
- Node.js 應用全鏈路追蹤技術——[全鏈路資訊獲取]Node.js
- 十分鐘實現滅霸打響指灰飛煙滅的效果
- 消滅又臭又長的if-else
- 微服務全鏈路灰度新能力微服務
- 微服務的全鏈路日誌微服務
- 端到端網路全鏈路監控方案
- 模擬積體電路設計系列部落格——6.2.5 毛刺
- 從三萬英尺看全鏈路灰度
- 微服務的全鏈路請求(RequestContextHolder)微服務Context
- 有贊全鏈路壓測 - 張弛
- 全鏈路效能測試怎麼做?
- 有贊全鏈路壓測實戰
- 私域運營的重要利器-企業微信助力消費品企業全鏈路獲客
- 深入剖析全鏈路灰度技術內幕
- 全鏈路壓測落地和演進之路
- 全鏈路壓測自動化實踐
- HBase2.0中的Benchmark工具—PerformanceEvaluationORM
- 效能測試 —— 什麼是全鏈路壓測?
- 構建資料紐帶:全鏈路血緣
- 構建基於 Ingress 的全鏈路灰度能力
- Rust 語言的全鏈路追蹤庫 tracingRust
- 淺析微服務全鏈路灰度解決方案微服務
- 全鏈路壓測(11):聊聊穩定性預案
- 生產全鏈路壓測常態化方案
- 全鏈路風控解決方案深度解讀
- 01 . 全鏈路監控CAT簡介及部署
- 全鏈路非同步Rest客戶端 ESA RestClient非同步REST客戶端client