Hbase高併發讀寫優化
淘寶搜尋的個性化離線實時分析系統Pora已升級至Pora2,Pora2是在基於Yarn的流式計算框架IStream基礎上開發的,同時為保證資料和訊息的實時處理系統中較多地使用了Hbase,是一個典型的高併發讀寫HBase的分散式應用。
系統在釋出之初遇到了比較嚴重的效能問題,表現為處理速度跟不上實時日誌,並且整個Hadoop/HBase叢集壓力大,連帶其它應用受影響。經過排查發現問題主要都出現在了對HBase的使用上,現將遇到的幾個典型的使用HBase的問題總結如下,希望能為其它類似應用正確使用HBase提供參考。
HBase的Periodic Flusher
從系統的各種統計指標分析,系統主要慢在了讀寫HBase的環節,觀察HBase日誌發現每個RegionServer都在頻繁地flush和compact。分析後發現當前hbase版本里有一個Periodic Flusher的機制,memstore中的資料如果持續一段時間沒有flush的話hbase會自動觸發flush,這個時間間隔預設是1小時。瞭解了一下這是hbase 0.94.8以後引入的新feature,初衷是防止有些memstore長時間不flush,在沒有開啟wal且遇到region server掛掉時導致資料丟失。
由於我們的 hbase每個region server有將近100個region,幾乎每分鐘都有region因為達到一小時的時間間隔觸發flush,而多數情況下每次flush的檔案都很小,flush次數多了之後又會引起compaction,如此頻繁的flush和compaction使得region server處理速度明顯變慢。在我們將這個配置調整為10小時後,可以從下圖中看到hbase flush queue size和fs pread latency都有明顯變小。
注:個人覺得hbase的這個新feature應用場景很有限,實在不應該作為一個預設開啟的配置,建議可以通過配置直接禁用。
不間斷的頻繁scan對region server造成較大壓力
Pora2採用了一個基於HBase實現的訊息佇列Hqueue,下游使用方通過讀這個訊息佇列,第一時間獲取最新的訊息進行處理。
讀訊息的過程相當於一次Scan,在一開始的Pora2版本中我們未對讀Hqueue的頻率進行控制,導致某些讀HQueue的worker不停地發起新的Scan,即便Hqueue資料已經讀完,仍會立刻重新建立Scan,而這個Scan會因為讀不到資料很快結束之後又重新建立。這樣不停地新建Scanner對Region Server端構成了不小的且是不必要的壓力。
發現這個問題後我們修改了這部分程式程式碼,在讀完資料後sleep幾秒後再重新scan。
在修改完hbase的配置並且加上讀Hqueue的scan頻率控制後,Pora2情況明顯好轉,但處理累積資料時的速度還是不夠快。
超大併發下的hbase問題
從統計指標上分析,系統還是慢在訪問hbase上,訪問hbase的日誌中不時出現各種TimeOutException也可以說明這一點。跟蹤日誌中某些頻繁報超時的regionserver發現,有些region server日誌持續報以下異常:
所在機器連線數很多,但load很低,而一旦pora2停了,這些現象就會很快消失。
從這一現象分析判斷是Pora2對HBase的併發連線數太多了,使得region server的handler不夠用,server端還沒來得及處理請求,client端已經到了超時時間而斷開。
為此我們大幅度減少了訪問hbase的程序數以減少對hbase的併發連線,為了不降低處理能力,在程序內部使用更多的處理執行緒。由於執行緒間是共享對hbase的連線的,所以增加執行緒數不會增加對hbase的連線數。
通過這一調整,很大程度緩解了hbase region server的壓力。
避免HBase訪問熱點
在作了較多優化改進後發現仍有幾個worker比較慢,跟蹤那幾個慢的worker日誌發現讀HBase經常超時,找到超時的region server,從HMaster UI上觀察到這個server的讀寫請求數明顯是其它server的好幾倍。開始懷疑是資料有傾斜,有熱點region落到了這臺機器上。在HBase UI上逐個檢查Pora2用到的HBase表,果然在其中的一張表上發現它的第一個region的請求數比其它region高出一兩個數量級。
按我們的設計預期,這個表的rowkey是加了hash字首的,理論上不該有熱點region,最終檢查程式碼後才發現是生成rowkey的程式碼存在bug,生成字首的程式碼用了 key.hashCode() % regionNum,結果有很多key的hashcode返回是負數,使得很多字首是負數,全都落在了第一個region上。
而對hbase來說,一旦有一個region有熱點,就會導致該region所在的region server變慢,進而使得這個server上其它表的region訪問也慢,從而影響了整個hbase的效能。
Bulk load資料的Major Compaction
調查中還發現了一個Bulk load資料未執行Major Compaction引起的問題。
我們有一張表的資料是每天定時從HDFS中Bulk load到HBase裡的,而且這張表的資料是隻讀的。結果幾天下來後,因為沒有資料寫入,不會觸發任何compaction,而每天都有新的HFile被Load進來,導致每個region下的HFile數越來越多,最終拖慢了這張表的讀取效能。
發現問題後我們在每天的Bulk load結束後對這個表執行一次Major Compaction,有效解決了問題。
總結
高併發讀寫HBase的應用需要儘量保證對HBase的合理使用,不合理的使用有可能會導致某一個region server甚至整個hbase叢集的效能出現問題,而hbase的效能問題又反過來使得所有應用效能下降,此時如果應用選擇繼續加大對hbase的併發訪問,甚至有可能因此陷入一個性能繼續變差的惡性迴圈。