HBase原理、設計與優化實踐
1、HBase 簡介
HBase —— Hadoop Database的簡稱,Google BigTable的另一種開源實現方式,從問世之初,就為了解決用大量廉價的機器高速存取海量數據、實現數據分布式存儲提供可靠的方案。從功能上來 講,HBase不折不扣是一個數據庫,與我們熟悉的Oracle、MySQL、MSSQL等一樣,對外提供數據的存儲和讀取服務。而從應用的角度來 說,HBase與一般的數據庫又有所區別,HBase本身的存取接口相當簡單,不支持復雜的數據存取,更不支持SQL等結構化的查詢語言;HBase也沒 有除了rowkey以外的索引,所有的數據分布和查詢都依賴rowkey。所以,HBase在表的設計上會有很嚴格的要求。架構上,HBase是分布式數 據庫的典范,這點比較像MongoDB的sharding模式,能根據鍵值的大小,把數據分布到不同的存儲節點上,MongoDB根據 configserver來定位數據落在哪個分區上,HBase通過訪問Zookeeper來獲取-ROOT-表所在地址,通過-ROOT-表得到相 應.META.表信息,從而獲取數據存儲的region位置。
2、架構
上面提到,HBase是一個分布式的架構,除去底層存儲的HDFS外,HBase本身從功能上可以分為三塊:Zookeeper群、Master群和RegionServer群。
-
Zookeeper群:HBase集群中不可缺少的重要部分,主要用于存儲Master地址、協調Master和RegionServer等上下線、存儲臨時數據等等。
</li> -
Master群:Master主要是做一些管理操作,如:region的分配,手動管理操作下發等等,一般數據的讀寫操作并不需要經過Master集群,所以Master一般不需要很高的配置即可。
</li> -
RegionServer群:RegionServer群是真正數據存儲的地方,每個RegionServer由若干個region組成,而一個region維護了一定區間rowkey值的數據,整個結構如下圖:
</li> -
</li> </ul>
HBase結構圖
上 圖中,Zookeeper(簡稱ZK)是一個集群,通常有奇數個ZK服務組成。Master為了服務可用性,也建議部署成集群方式,因為Master是整 個管理操作的發起者,如果Master一旦發生意外停機,整個集群將會無法進行管理操作,所以Master也必須有多個,當然多個Master也有主從之 分,如何區分哪個是主,哪個是從?關鍵看哪個Master能競爭到ZK上對應Master目錄下的鎖,持有該目錄鎖的Master為主Master,其他 從Master輪詢競爭該鎖,所以一旦主Master發生意外停機,從Master很快會因為競爭到Master文件夾上的鎖而接管服務。
RegionServer(簡 稱RS)在非Replication模式下,整個系統中都是唯一的,也就是說,在整個非Replication的HBase集群中,每臺RS上保存的數據 都不一樣,所以相對于前面兩者,該模式下的RS并不是高可用的,至少RS可能存在單點故障的問題,但是由于HBase內部數據分region存儲和 region可以遷移的機制,RS服務的單點故障可能會在極小代價下很快恢復,但是一旦停掉的RS上有-ROOT-或者.META.表的region,那 后果還是比較嚴重,因為數據節點的RS停機,只會在短時間內影響該臺RS上的region不可訪問,等到region遷移完成后即可恢復,如果是 -ROOT-、.META.所在的RS停機,整個HBase的新的求情都將受到影響,因為需要通過.META.表來路由,從而尋找到region所在RS 的地址。3、數據組織
整個架構中,ZK用于服務協調和整個集群運行過程中部分信息的保存和-ROOT-表地址定位,Master用于集群內部管理,所以剩下的RS主要用于處理數據。
RS 是處理數據的主要場所,那么在RS內部的數據是怎么分布的?其實RS本身只是一個容器,其定義了一些功能線程,比如:數據合并線程(compact thread)、storeFile分割線程(split thread)等等。容器中的主要對象就是region,region是一個表根據自身rowkey范圍劃分的一部分,一個表可以被劃分成若干部分,也就 是若干個region,region可以根據rowkey范圍不同而被分布在不同的RS上(當然也可以在同一個RS上,但不建議這么做)。一個RS上可以 包含多個表的region,也可以只包含一個表的部分region,RS和表是兩個不同的概念。
這里還有一個概念——列簇。對HBase有一些了 解的人,或多或少聽說過:HBase是一個列式存儲的數據庫,而這個列式存儲中的列,其實是區別于一般數據庫的列,這里的列的概念,就是列簇,列簇,顧名 思義就是很多列的集合,而在數據存儲上來講,不同列簇的數據,一定是分開存儲的,即使是在同一個region內部,不同的列簇也存儲在不同的文件夾中,這 樣做的好處是,一般我們定義列簇的時候,通常會把類似的數據放入同一個列簇,不同的列簇分開存儲,有利于數據的壓縮,并且HBase本身支持多種壓縮方 式。4、原理
前面介紹了HBase的一般架構,我們知道了HBase有ZK、Master和RS等組成,本節我們來介紹下HBase的基本原理,從數據訪問、RS路由到RS內部緩存、數據存儲和刷寫再到region的合并和拆分等等功能。
4.1 RegionServer定位
訪 問HBase通過HBase客戶端(或API)進行,整個HBase提供給外部的地址,其實是ZK的入口,前面也介紹了,ZK中有保存-ROOT-所在的 RS地址,從-ROOT-表可以獲取.META.表信息,根據.META.表可以獲取region在RS上的分布,整個region尋址過程大致如下:
RS定位過程
-
首先,Client通過訪問ZK來請求目標數據的地址。
</li> -
ZK中保存了-ROOT-表的地址,所以ZK通過訪問-ROOT-表來請求數據地址。
</li> -
同樣,-ROOT-表中保存的是.META.的信息,通過訪問.META.表來獲取具體的RS。
</li> -
.META.表查詢到具體RS信息后返回具體RS地址給Client。
</li> -
Client端獲取到目標地址后,然后直接向該地址發送數據請求。
</li> </ol>上述過程其實是一個三層索引結構,從ZK獲取-ROOT-信息,再從-ROOT-獲取.META.表信息,最后從.META.表中查到RS地址后緩存。這里有幾個問題:
-
既然ZK中能保存-ROOT-信息,那么為什么不把.META.信息直接保存在ZK中,而需要通過-ROOT-表來定位?
</li> -
Client查找到目標地址后,下一次請求還需要走ZK —> -ROOT- —> .META.這個流程么?
</li> </ul>先 來回答第一個問題:為什么不直接把.META.表信息直接保存到ZK中?主要是為了保存的數據量考慮,ZK中不宜保存大量數據,而.META.表主要是保 存Region和RS的映射信息,region的數量沒有具體約束,只要在內存允許的范圍內,region數量可以有很多,如果保存在ZK中,ZK的壓力 會很大。所以,通過一個-ROOT-表來轉存到RS中是一個比較理想的方案,相比直接保存在ZK中,也就多了一層-ROOT-表的查詢,對性能來說影響不 大。
第二個問題:每次訪問都需要走ZK –> -ROOT- —> .META.的流程么?當然不需要,Client端有緩存,第一次查詢到相應region所在RS后,這個信息將被緩存到Client端,以后每次訪問都 直接從緩存中獲取RS地址即可。當然這里有個意外:訪問的region若果在RS上發生了改變,比如被balancer遷移到其他RS上了,這個時候,通 過緩存的地址訪問會出現異常,在出現異常的情況下,Client需要重新走一遍上面的流程來獲取新的RS地址。總體來說,region的變動只會在極少數 情況下發生,一般變動不會很大,所以在整個集群訪問過程中,影響可以忽略。4.2 Region數據寫入
HBase通過ZK —> -ROOT- —> .META.的訪問獲取RS地址后,直接向該RS上進行數據寫入操作,整個過程如下圖:
RegionServer數據操作過程
Client 通過三層索引獲得RS的地址后,即可向指定RS的對應region進行數據寫入,HBase的數據寫入采用WAL(write ahead log)的形式,先寫log,后寫數據。HBase是一個append類型的數據庫,沒有關系型數據庫那么復雜的操作,所以記錄HLog的操作都是簡單的 put操作(delete/update操作都被轉化為put進行)
4.3 HLog
4.3.1 HLog寫入
HLog 是HBase實現WAL方式產生的日志信息,其內部是一個簡單的順序日志,每個RS上的region都共享一個HLog,所有對于該RS上的region 數據寫入都被記錄到該HLog中。HLog的主要作用就是在RS出現意外崩潰的時候,可以盡量多的恢復數據,這里說是盡量多,因為在一般情況下,客戶端為 了提高性能,會把HLog的auto flush關掉,這樣HLog日志的落盤全靠操作系統保證,如果出現意外崩潰,短時間內沒有被fsync的日志會被丟失。
4.3.2 HLog過期
HLog的大量寫入會造成HLog占用存儲空間會越來越大,HBase通過HLog過期的方式進行HLog的清理,每個RS內部都有一個HLog監控線程在運行,其周期可以通過hbase.master.cleaner.interval進行配置。
HLog 在數據從memstore flush到底層存儲上后,說明該段HLog已經不再被需要,就會被移動到.oldlogs這個目錄下,HLog監控線程監控該目錄下的HLog,當該文 件夾下的HLog達到hbase.master.logcleaner.ttl設置的過期條件后,監控線程立即刪除過期的HLog。4.4 Memstore
4.4.1 數據存儲
memstore 是region內部緩存,其大小通過HBase參數hbase.hregion.memstore.flush.size進行配置。RS在寫完HLog以 后,數據寫入的下一個目標就是region的memstore,memstore在HBase內部通過LSM-tree結構組織,所以能夠合并大量對于相 同rowkey上的更新操作。
正是由于memstore的存在,HBase的數據寫入都是異步的,而且性能非常不錯,寫入到memstore后, 該次寫入請求就可以被返回,HBase即認為該次數據寫入成功。這里有一點需要說明,寫入到memstore中的數據都是預先按照rowkey的值進行排 序的,這樣有利于后續數據查找。4.4.2 數據刷盤
memstore中的數據在一定條件下會進行刷寫操作,使數據持久化到相應的存儲設備上,觸發memstore刷盤的操作有多種不同的方式如下圖:
Memstore刷寫流程
以上1,2,3都可以觸發memstore的flush操作,但是實現的方式不同:
-
1通過全局內存控制,觸發memstore刷盤操作。memstore整體內存占用上限通過參數 hbase.regionserver.global.memstore.upperLimit進行設置,當然在達到上限后,memstore的刷寫也不 是一直進行,在內存下降到hbase.regionserver.global.memstore.lowerLimit配置的值后,即停止 memstore的刷盤操作。這樣做,主要是為了防止長時間的memstore刷盤,會影響整體的性能。
</li> -
在該種情況下,RS中所有region的memstore內存占用都沒達到刷盤條件,但整體的內存消耗已經到一個非常危險的范圍,如果持續下去,很有可能造成RS的OOM,這個時候,需要進行memstore的刷盤,從而釋放內存。
</li> -
2手動觸發memstore刷盤操作
</li> -
HBase提供API接口,運行通過外部調用進行memstore的刷盤
</li> -
3 memstore上限觸發數據刷盤
</li> -
前面提到memstore的大小通過hbase.hregion.memstore.flush.size進行設置,當region中memstore的數據量達到該值時,會自動觸發memstore的刷盤操作。
</li> </ul>4.4.3 刷盤影響
memstore 在不同的條件下會觸發數據刷盤,那么整個數據在刷盤過程中,對region的數據寫入等有什么影響?memstore的數據刷盤,對region的直接影 響就是:在數據刷盤開始到結束這段時間內,該region上的訪問都是被拒絕的,這里主要是因為在數據刷盤結束時,RS會對改region做一個 snapshot,同時HLog做一個checkpoint操作,通知ZK哪些HLog可以被移到.oldlogs下。從前面圖上也可以看到,在 memstore寫盤開始,相應region會被加上UpdateLock鎖,寫盤結束后該鎖被釋放。
4.5 StoreFile
memstore 在觸發刷盤操作后會被寫入底層存儲,每次memstore的刷盤就會相應生成一個存儲文件HFile,storeFile即HFile在HBase層的輕 量級分裝。數據量的持續寫入,造成memstore的頻繁flush,每次flush都會產生一個HFile,這樣底層存儲設備上的HFile文件數量將 會越來越多。不管是HDFS還是Linux下常用的文件系統如Ext4、XFS等,對小而多的文件上的管理都沒有大文件來的有效,比如小文件打開需要消耗 更多的文件句柄;在大量小文件中進行指定rowkey數據的查詢性能沒有在少量大文件中查詢來的快等等。
4.6 Compact
大量HFile的產生,會消耗更多的文件句柄,同時會造成RS在數據查詢等的效率大幅度下降,HBase為解決這個問題,引入了compact操作,RS通過compact把大量小的HFile進行文件合并,生成大的HFile文件。
RS上的compact根據功能的不同,可以分為兩種不同類型,即:minor compact和major compact。-
Minor Compact
</li> </ul>minor compact又叫small compact,在RS運行過程中會頻繁進行,主要通過參數hbase.hstore.compactionThreshold進行控制,該參數配置了 HFile數量在滿足該值時,進行minor compact,minor compact只選取region下部分HFile進行compact操作,并且選取的HFile大小不能超過 hbase.hregion.max.filesize參數設置。
-
Major Compact
</li> </ul>相 反major compact也被稱之為large compact,major compact會對整個region下相同列簇的所有HFile進行compact,也就是說major compact結束后,同一個列簇下的HFile會被合并成一個。major compact是一個比較長的過程,對底層I/O的壓力相對較大。
major compact除了合并HFile外,另外一個重要功能就是清理過期或者被刪除的數據。前面提到過,HBase的delete操作也是通過append的 方式寫入,一旦某些數據在HBase內部被刪除了,在內部只是被簡單標記為刪除,真正在存儲層面沒有進行數據清理,只有通過major compact對HFile進行重組時,被標記為刪除的數據才能被真正的清理。
compact操作都有特定的線程進行,一般情況下不會影響RS上 數據寫入的性能,當然也有例外:在compact操作速度跟不上region中HFile增長速度時,為了安全考慮,RS會在HFile達到一定數量時, 對寫入進行鎖定操作,直到HFile通過compact降到一定的范圍內才釋放鎖。4.7 Split
compact 將多個HFile合并單個HFile文件,隨著數據量的不斷寫入,單個HFile也會越來越大,大量小的HFile會影響數據查詢性能,大的HFile也 會,HFile越大,相對的在HFile中搜索的指定rowkey的數據花的時間也就越長,HBase同樣提供了region的split方案來解決大的 HFile造成數據查詢時間過長問題。
一個較大的region通過split操作,會生成兩個小的region,稱之為Daughter,一般Daughter中的數據是根據rowkey的之間點進行切分的,region的split過程大致如下圖:
region split流程
-
region先更改ZK中該region的狀態為SPLITING。
</li> -
Master檢測到region狀態改變。
</li> -
region會在存儲目錄下新建.split文件夾用于保存split后的daughter region信息。
</li> -
Parent region關閉數據寫入并觸發flush操作,保證所有寫入Parent region的數據都能持久化。
</li> -
在.split文件夾下新建兩個region,稱之為daughter A、daughter B。
</li> -
Daughter A、Daughter B拷貝到HBase根目錄下,形成兩個新的region。
</li> -
Parent region通知修改.META.表后下線,不再提供服務。
</li> -
Daughter A、Daughter B上線,開始向外提供服務。
</li> -
如果開啟了balance_switch服務,split后的region將會被重新分布。
</li> </ol>上面1 ~ 9就是region split的整個過程,split過程非常快,速度基本會在秒級內,那么在這么快的時間內,region中的數據怎么被重新組織的?
其 實,split只是簡單的把region從邏輯上劃分成兩個,并沒有涉及到底層數據的重組,split完成后,Parent region并沒有被銷毀,只是被做下線處理,不再對外部提供服務。而新產生的region Daughter A和Daughter B,內部的數據只是簡單的到Parent region數據的索引,Parent region數據的清理在Daughter A和Daughter B進行major compact以后,發現已經沒有到其內部數據的索引后,Parent region才會被真正的清理。5、HBase設計
HBase是一個分布式數據庫,其性能的好壞主要取決于內部表的設計和資源的分配是否合理。
5.1 Rowkey設計
rowkey 是HBase實現分布式的基礎,HBase通過rowkey范圍劃分不同的region,分布式系統的基本要求就是在任何時候,系統的訪問都不要出現明顯 的熱點現象,所以rowkey的設計至關重要,一般我們建議rowkey的開始部分以hash或者MD5進行散列,盡量做到rowkey的頭部是均勻分布 的。禁止采用時間、用戶id等明顯有分段現象的標志直接當作rowkey來使用。
5.2 列簇設計
HBase的表設計時,根據不同需求有不同選擇,需要做在線查詢的數據表,盡量不要設計多個列簇,我們知道,不同的列簇在存儲上是被分開的,多列簇設計會造成在數據查詢的時候讀取更多的文件,從而消耗更多的I/O。
5.3 TTL設計
選擇合適的數據過期時間也是表設計中需要注意的一點,HBase中允許列簇定義數據過期時間,數據一旦超過過期時間,可以被major compact進行清理。大量無用歷史數據的殘余,會造成region體積增大,影響查詢效率。
5.4 Region設計
一 般地,region不宜設計成很大,除非應用對階段性性能要求很多,但是在將來運行一段時間可以接受停服處理。region過大會導致major compact調用的周期變長,而單次major compact的時間也相應變長。major compact對底層I/O會造成壓力,長時間的compact操作可能會影響數據的flush,compact的周期變長會導致許多刪除或者過期的數據 不能被及時清理,對數據的讀取速度等都有影響。
相反,小的region意味著major compact會相對頻繁,但是由于region比較小,major compact的相對時間較快,而且相對較多的major compact操作,會加速過期數據的清理。
當 然,小region的設計意味著更多的region split風險,region容量過小,在數據量達到上限后,region需要進行split來拆分,其實split操作在整個HBase運行過程中,是 被不怎么希望出現的,因為一旦發生split,涉及到數據的重組,region的再分配等一系列問題。所以我們在設計之初就需要考慮到這些問題,盡量避免 region的運行過程中發生split。
HBase可以通過在表創建的時候進行region的預分配來解決運行過程中region的split 產生,在表設計的時候,預先分配足夠多的region數,在region達到上限前,至少有部分數據會過期,通過major compact進行清理后, region的數據量始終維持在一個平衡狀態。
region數量的設計還需要考慮內存上的限制,通過前面的介紹我們知道每個region都有memstore,memstore的數量與region數量和region下列簇的數量成正比,一個RS下memstore內存消耗:Memory = memstore大小 * region數量 * 列簇數量
如果不進行前期數據量估算和region的預分配,通過不斷的split產生新的region,容易導致因為內存不足而出現OOM現象。
6、優化背景與歷史現狀
Datastream 一直以來在使用HBase分流日志,每天的數據量很大,日均大概在80億條,10TB的數據。對于像Datastream這種數據量巨大、對寫入要求非常 高,并且沒有復雜查詢需求的日志系統來說,選用HBase作為其數據存儲平臺,無疑是一個非常不錯的選擇。
HBase 是一個相對較復雜的分布式系統,并發寫入的性能非常高。然而,分布式系統從結構上來講,也相對較復雜,模塊繁多,各個模塊之間也很容易出現一些問題,所以 對像HBase這樣的大型分布式系統來說,優化系統運行,及時解決系統運行過程中出現的問題也變得至關重要。正所謂:“你”若安好,便是晴天;“你”若有 恙,我便沒有星期天。
HBase 交接到我們團隊手上時,已經在線上運行有一大段時間了,期間也偶爾聽到過系統不穩定的、時常會出現一些問題的言論,但我們認為:一個能被大型互聯網公司廣 泛采用的系統(包括非死book,推ter,淘寶,小米等),其在性能和可用性上是毋庸置疑的,何況像非死book這種公司,是在經過嚴格 選型后,放棄了自己開發的Cassandra系統,用HBase取而代之。既然這樣,那么,HBase的不穩定、經常出問題一定有些其他的原因,我們所要 做的,就是找出這些HBase的不穩定因素,還HBase一個“清白”。“查案”之前,先來簡單回顧一下我們接手HBase時的現狀(我們運維著好幾個 HBase集群,這里主要介紹問題最多那個集群的調優):
名稱 數量 備注 </tr>服務器數量 17 配置不同,HBase、HDFS都部署在這些機器上 </tr>表數量 30+ 只有部分表的數據量比較大,其他基本沒多少數據 </tr>Region數量 600+ 基本上都是數據量較大的表劃分的region較多 </tr>請求量 50000+ 服務器請求分布極其不均勻 </tr> </tbody> </table>應用反應經常會過段時間出現數據寫入緩慢,導致應用端數據堆積現象,是否可以通過增加機器數量來解決?
其 實,那個時候,我們本身對HBase也不是很熟悉,對HBase的了解,也僅僅在做過一些測試,了解一些性能,對內部結構,實現原理之類的基本上都不怎么 清楚。于是剛開始幾天,各種問題,每天晚上拉著一男一起摸索,順利的時候,晚上8,9點就可以暫時搞定線上問題,更多的時候基本要到22點甚至更晚(可能 那個時候流量也下去了),通過不斷的摸索,慢慢了解HBase在使用上的一些限制,也就能逐漸解決這一系列過程中發現的問題。后面挑幾個相對比較重要,效 果較為明顯的改進點,做下簡單介紹。
7、調優
首先根據目前17臺機器,50000+的QPS,并且觀察磁盤的I/O利用率和CPU利用率都相當低來判斷:當前的請求數量根本沒有達到系統的性能瓶頸,不需要新增機器來提高性能。如果不是硬件資源問題,那么性能的瓶頸究竟是什么?
7.1 Rowkey設計問題
7.1.1 現象
打開HBase的Web端,發現HBase下面各個RegionServer的請求數量非常不均勻,第一個想到的就是HBase的熱點問題,具體到某個具體表上的請求分布如下:
HBase表請求分布
上面是HBase下某張表的region請求分布情況,從中我們明顯可以看到,部分region的請求數量為0,而部分的請求數量可以上百萬,這是一個典型的熱點問題。
7.1.2 原因
HBase 出現熱點問題的主要原因無非就是rowkey設計的合理性,像上面這種問題,如果rowkey設計得不好,很容易出現,比如:用時間戳生成rowkey, 由于時間戳在一段時間內都是連續的,導致在不同的時間段,訪問都集中在幾個RegionServer上,從而造成熱點問題。
7.1.3 解決
知道了問題的原因,對癥下藥即可,聯系應用修改rowkey規則,使rowkey數據隨機均勻分布,效果如下:
Rowkey重定義后請求分布
7.1.4 建議
對于HBase來說,rowkey的范圍劃定了RegionServer,每一段rowkey區間對應一個RegionServer,我們要保證每段時間內的rowkey訪問都是均勻的,所以我們在設計的時候,盡量要以hash或者md5等開頭來組織rowkey。
編者注:如果業務需求是按時間范圍批量或者最左前綴查詢呢?此時你hash或者md5必然帶來效率下降,這種場景可以思考下如何來優化呢?
7.2 Region重分布
7.2.1 現象
HBase 的集群是在不斷擴展的,分布式系統的最大好處除了性能外,不停服橫向擴展也是其中之一,擴展過程中有一個問題:每次擴展的機器的配置是不一樣的,一般,后 面新加入的機器性能會比老的機器好,但是后面加入的機器經常被分配很少的region,這樣就造成了資源分布不均勻,隨之而來的就是性能上的損失,如下:
HBase各個RegionServer請求
上圖中我們可以看到,每臺RegionServer上的請求極為不均勻,多的好幾千,少的只有幾十
7.2.2 原因
資源分配不均勻,造成部分機器壓力較大,部分機器負載較低,并且部分Region過大過熱,導致請求相對較集中。
7.2.3 解決
遷移部分老的RegionServer上的region到新加入的機器上,使每個RegionServer的負載均勻。通過split切分部分較大region,均勻分布熱點region到各個RegionServer上。
HBase region請求分布
對比前后兩張截圖我們可以看到,Region總數量從1336增加到了1426,而增加的這90個region就是通過split切分大的region得到的。而對region重新分布后,整個HBase的性能有了大幅度提高。
7.2.4 建議
Region 遷移的時候不能簡單開啟自動balance,因為balance主要的問題是不會根據表來進行balance,HBase的自動balance只會根據每 個RegionServer上的Region數量來進行balance,所以自動balance可能會造成同張表的region會被集中遷移到同一個臺 RegionServer上,這樣就達不到分布式的效果。
基本上,新增RegionServer后的region調整,可以手工進行,盡量使表的Region都平均分配到各個RegionServer上,另外一點,新增的RegionServer機器,配置最好與前面的一致,否則資源無法更好利用。
對于過大,過熱的region,可以通過切分的方法生成多個小region后均勻分布(注意:region切分會觸發major compact操作,會帶來較大的I/O請求,請務必在業務低峰期進行)
7.3 HDFS寫入超時
7.3.1 現象
HBase寫入緩慢,查看HBase日志,經常有慢日志如下:
WARN org.apache.hadoop.ipc.HBaseServer- (responseTooSlow): {“processingtimems”:36096, “call”:”multi(org.apache.hadoop.hbase.client.MultiAction@7884377e), rpc version=1, client version=29, methodsFingerPrint=1891768260″, “client”:”xxxx.xxx.xxx.xxxx:44367″, “starttimems”:1440239670790, “queuetimems”:42081, “class”:”HRegionServer”, “responsesize”:0, “method”:”multi”}
并且伴有HDFS創建block異常如下:
INFO org.apache.hadoop.hdfs.DFSClient – Exception in createBlockOutputStream
org.apache.hadoop.hdfs.protocol.HdfsProtoUtil.vintPrefixed(HdfsProtoUtil.java:171)
org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.createBlockOutputStream(DFSOutputStream.java:1105)
org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.nextBlockOutputStream(DFSOutputStream.java:1039)
org.apache.hadoop.hdfs.DFSOutputStream$DataStreamer.run(DFSOutputStream.java:487)
一般地,HBase客戶端的寫入到RegionServer下某個region的memstore后就返回,除了網絡外,其他都是內存操作,應該不會有長達30多秒的延遲,外加HDFS層拋出的異常,我們懷疑很可能跟底層數據存儲有關。
7.3.2 原因
定 位到可能是HDFS層出現了問題,那就先從底層開始排查,發現該臺機器上10塊盤的空間利用率都已經達到100%。按理說,作為一個成熟的分布式文件系 統,對于部分數據盤滿的情況,應該有其應對措施。的確,HDFS本身可以設置數據盤預留空間,如果部分數據盤的預留空間小于該值時,HDFS會自動把數據 寫入到另外的空盤上面,那么我們這個又是什么情況?
最 終通過多方面的溝通確認,發現了主要原因:我們這批機器,在上線前SA已經經過處理,每塊盤默認預留100G空間,所以當通過df命令查看盤使用率為 100%時,其實盤還有100G的預留空間,而HDFS層面我們配置的預留空間是50G,那么問題就來了:HDFS認為盤還有100G空間,并且多于 50G的預留,所以數據可以寫入本地盤,但是系統層面卻禁止了該寫入操作,從而導致數據寫入異常。
7.3.3 解決
解 決的方法可以讓SA釋放些空間出來便于數據寫入。當然,最直接有效的就是把HDFS的預留空間調整至100G以上,我們也正是這樣做的,通過調整后,異常 不再出現,HBase層面的slow log也沒有再出現。同時我們也開啟了HDFS層面的balance,使數據自動在各個服務器之間保持平衡。
7.3.4 建議
磁盤滿了導致的問題很難預料,HDFS可能會導致部分數據寫入異常,MySQL可能會出現直接宕機等等,所以最好的辦法就是:不要使盤的利用率達到100%。
7.4 網絡拓撲
7.4.1 現象
通過rowkey調整,HDFS數據balance等操作后,HBase的確穩定了許多,在很長一段時間都沒有出現寫入緩慢問題,整體的性能也上漲了很多。但時常會隔一段時間出現些slow log,雖然對整體的性能影響不大,但性能上的抖動還是很明顯。
7.4.2 原因
由 于該問題不經常出現,對系統的診斷帶來不小的麻煩,排查了HBase層和HDFS層,幾乎一無所獲,因為在大多數情況下,系統的吞吐量都是正常的。通過腳 本收集RegionServer所在服務器的系統資源信息,也看不出問題所在,最后懷疑到系統的物理拓撲上,HBase集群的最大特點是數據量巨大,在做 一些操作時,很容易把物理機的千兆網卡都吃滿,這樣如果網絡拓撲結構存在問題,HBase的所有機器沒有部署在同一個交換機上,上層交換機的進出口流量也 有可能存在瓶頸。網絡測試還是挺簡單的,直接ping就可以,我們得到以下結果:共17臺機器,只有其中一臺的延遲存在問題,如下:
網絡延遲測試:Ping結果
同一個局域網內的機器,延遲達到了毫秒級別,這個延遲是比較致命的,因為分布式存儲系統HDFS本身對網絡有要求,HDFS默認3副本存在不同的機器上,如果其中某臺機器的網絡存在問題,這樣就會影響到該機器上保存副本的寫入,拖慢整個HDFS的寫入速度。
7.4.3 解決
網絡問題,聯系機房解決,機房的反饋也驗證了我們的想法:由于HBase的機器后面進行了擴展,后面加入的機器有一臺跟其他機器不在同一個交換機下,而這臺機器正是我們找出的有較大ping延時這臺,整個HBase物理結構如下:
HBase物理拓撲結構
跟機房協調,調整機器位置,使所有的HBase機器都位于同一個交換機下,問題迎刃而解。
7.4.4 建議
對于分布式大流量的系統,除了系統本身,物理機的部署和流量規劃也相當重要,盡量使集群中所有的機器位于相同的交換機下(有容災需求的應用除外),集群較大,需要跨交換機部署時,也要充分考慮交換機的出口流量是否夠用,網絡硬件上的瓶頸診斷起來相對更為困難。
7.5 JVM參數調整
解決了網絡上面的不穩定因素,HBase的性能又得到進一步的提高,隨之也帶來了另外的問題。
7.5.1 現象
根據應用反應,HBase會階段性出現性能下降,導致應用數據寫入緩慢,造成應用端的數據堆積,這又是怎么回事?經過一系列改善后HBase的系統較之以前有了大幅度增長,怎么還會出現數據堆積的問題?為什么會階段性出現?
HBase流量統計
從上圖看,HBase平均流量QPS基本能達到12w,但是每過一段時間,流量就會下降到接近零點,同時這段時間,應用會反應數據堆積。
7.5.2 原因
這個問題定位相對還是比較簡單,結合HBase的日志,很容易找到問題所在:
org.apache.hadoop.hbase.util.Sleeper – We slept 41662ms instead of 3000ms, this is likely due to a long garbage collecting pause and it’s usually bad
通過上述日志,基本上可以判定是HBase的某臺RegionServer出現GC問題,導致了服務在很長一段時間內禁止訪問。
HBase通過一系列的調整后,整個系統的吞吐量增加了好幾倍,然而JVM的堆大小沒有進行相應的調整,整個系統的內存需求變大,而虛擬機又來不及回收,最終導致出現Full GC
7.5.3 解決
GC問題導致HBase整個系統的請求下降,通過適當調整JVM參數的方式,解決HBase RegionServer的GC問題。
7.5.4 建議
對 于HBase來說,本身不存在單點故障,即使宕掉1,2臺RegionServer,也只是使剩下幾臺的壓力有所增加,不會導致整個集群服務能力下降很 多。但是,如果其中某臺RegionServer出現Full GC問題,那么這臺機器上所有的訪問都會被掛起,客戶端請求一般都是batch發送的,rowkey的隨機分布導致部分請求會落到該臺 RegionServer上,這樣該客戶端的請求就會被阻塞,導致客戶端無法正常寫數據到HBase。所以,對于HBase來說,宕機并不可怕,但長時間 的Full GC是比較致命的,配置JVM參數的時候,盡量要考慮避免Full GC的出現。
8、后記
經過前面一系列的優化,目前Datastream的這套HBase線上環境已經相當穩定,連續運行幾個月都沒有任何HBase層面由于系統性能不穩定導致的報警,平均性能在各個時間段都比較穩定,沒有出現過大幅度的波動或者服務不可用等現象。
Refer:
[1] HBase原理和設計
http://www.bitstech.net/2015/09/16/hbase-architecture/
[2] HBase優化實戰
http://www.bitstech.net/2015/12/04/hbase-optmization/
[3] HBase 數據庫檢索性能優化策略
https://www.ibm.com/developerworks/cn/java/j-lo-HBase/
[4] 一種基于HBase表的條件查詢優化方法
http://www.google.com/patents/CN103646073A?cl=zh
[5] HBase 實戰(2)--時間序列檢索和面檢索的應用場景實戰
http://www.cnblogs.com/mumuxinfei/p/3869998.html
[6] HBase性能優化方法總結(一):表的設計
http://www.cnblogs.com/panfeng412/archive/2012/03/08/hbase-performance-tuning-section1.html
[7] 日志系統之HBase日志存儲設計優化
http://blog.csdn.net/yanghua_kobe/article/details/46482319本文由用戶 nyyb 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!sesese色
-
-
-
-
-
-