hbase 錯誤調用表讀方法引發的血案

記一次錯誤調用hbase讀方法引發的血案

需求說明

目前公司的數據庫存在上前億級別的GPS座標點,數據量在幾十至百TB級別,這些座標需要獲取從百度、高德等網站上更新獲取該點對應的服務信息,即一個座標點對應一條該座標的描述信息。如果將這些座標全部按一個點一個 點 的查詢下載,按照目前我們的查詢Http接口帶寬限制,至少需要1年多。通過抽樣調研後發現,這些座標裏存在有大量的重複數據,重複率接近80%,若採用緩存的方式,存儲這些座標描述信息,當重複座標出現時,可以利用數據庫的查詢提高獲取座標的描述信息,同時節省大量的帶寬,考慮到數據庫將存儲上TB級別數據,自然想到hbase。

方案

利用hbase存儲更新後的gps座標點是不錯的選擇。目前集羣有11臺regionServer服務器,當讀寫hbase時發現最高讀數據接近50W 請求/s ,這對於重複數據的gps座標點直接讀取hbase更新,將省掉大量的資源和時間。

架構1

考慮到未來除了採用 hbase做緩衝存儲座標點外還可以用來做其他信息的存儲緩衝,於是我們提出瞭如下第一種架構方案 

我們將我們的緩存模塊單獨提取出來做成限速服務,限速服務負責這個HTTP的多線程併發查詢網絡資源和以及hbase的讀寫。這樣做的優點是具體的業務只需要負責向緩存模塊發送http請求,並不需要關心緩存模塊內部的實現細節,這樣充分實現了業務間的解耦;缺點是hbase的讀寫性能瓶頸將取決於緩存模塊的服務器和帶寬。

測試:

在本地測試發現同時啓動1500併發訪問該緩存模塊時,能正常的返回所有數據,但存在2-3秒左右的延時,但這絲毫不影響生產環境的使用,因爲生產環境下spark集羣的併發在200左右,遠遠小於1500,於是上線,上線後發現Hbase大量如下日誌:

INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #5,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #6,waiting  for 2100 actions to finish  on  table:TEST
INFO lient AsyncProcess: #1,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #8,waiting  for 950 actions to finish  on  table:TEST
INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #3,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #1,waiting  for 800 actions to finish  on  table:TEST
INFO lient AsyncProcess: #9,waiting  for 2100 actions to finish  on  table:TEST

同時發現hbase查詢80w+併到達峯值,最後直接導致hbase FULL GC並至集羣宕機。看到這個第一眼的 直覺是hbase沒有返回結果,導致client提交請求 堵塞了。經過各種google後,都沒找到滿意答案,將問題上報,此時各路不明情況的圍觀大神都出現了,有的說region server性能問題,先去調服務器參數,甚至有直接調zookeeper連接的,還有的調GC回收器都無濟於事。實在沒轍,難道是hbase client端connection過少,於是做各做連接池,都沒什麼用。看到有個大神寫的hbase 1.2版本的源碼發現,Hbase客服端創建連接池方法ConnectionManager.createConnection()實際都是同一個線程,hbase2.0以後版本 才能帶給大家驚喜,頓時心裏 涼涼,轉頭一想,難道是 我hbase client連接數太少導致的,死馬當作活馬醫,於是將hbase的讀寫模塊放至spark集羣中來緩解緩存模塊讀寫hbase的壓力,這樣會不會更好呢?

架構2

於是有了如下的版本 2:

(缺點)業務耦合性太強,當換成補充其他信息,重用緩存模塊時需要重新改寫客戶端。(優點):將hbase讀寫壓力分攤到集羣機器上,同時實現hbase與http模塊的解耦。

測試:

然後再將上述代碼提交運行,發現 ,spark集羣跑200-300G小規模數據在20W左右併發訪問hbase時,沒問題,一旦提高節點 個數,hbase 服務端的region Server瞬間端機,同時出現上圖一模一樣錯誤:

INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #5,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #5,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #5,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #5,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #5,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #5,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #5,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #2,waiting  for 1000 actions to finish  on  table:TEST
INFO lient AsyncProcess: #5,waiting  for 1000 actions to finish  on  table:TEST

不過這次 的 錯誤是waiting  for 1000 ...,想起在處理業務時曾經批量插入hbase時採用的正是1000條,於是將hbase每次插入的條數改爲 500、200、100都沒有用,而且hbase同樣是併發很高,頓時覺得不可思議。於是想到table.batch(batch, results)方法的問題,這個方法無論是網上還是官方api說明都是說批量讀能提高效率。然而這個方法改成get(list<Get>)後,問題解決。

table.batch(batch, results)是採用異步的方式讀,只也解釋了爲什麼之前每次100條數據提交hbase併發依舊很高的原因,因爲客戶端調用這個方法提交該批數據讀後,沒等到本次數據的讀結果返回,下次讀批又訪問hbase去了,最終導致hbase資源耗盡,集羣崩潰。

 

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