問題過程描述
? ? ? 最近在做關(guān)于HBase相關(guān)的工作,剛接入了1個關(guān)于數(shù)字證書的表,業(yè)務(wù)在灰度的時候發(fā)現(xiàn)有大量的超時的請求(備注:默認(rèn)3秒超時),如下圖,基本所有的請求都出現(xiàn)失?。?/p>

這個問題有點奇怪,在測試環(huán)境測試的時候沒有出現(xiàn)過這種超時的問題,通過hbase shell查詢數(shù)據(jù)也正常。目前的架構(gòu)如下:

從架構(gòu)上看各個方面假設(shè)都可以排除:
假設(shè)1、HBase后端的問題
? ? 如果是HBase有問題,那么應(yīng)該通過HBase shell訪問以及測試機(jī)器訪問(測試機(jī)器直接連接線上HBase測試,只有查詢的操作)也會有問題,但是通過HBase shell以及測試機(jī)器訪問都沒有出現(xiàn)超時的情況,因此HBase后端的問題基本可以排除。
假設(shè)2、Thrift Server的問題
? ? 因為通過LVS轉(zhuǎn)發(fā),其他的業(yè)務(wù)和數(shù)字證書的表共用Thrift Server,其他業(yè)務(wù)訪問HBase都正常,如果是Thrift Server有問題應(yīng)該其他的表也會出現(xiàn)大量超時的情況,線上其他的表都沒發(fā)現(xiàn)異常,因此這個基本也可以排除。
假設(shè)3、數(shù)據(jù)證書的表上線前沒有初始化?
? ? 數(shù)據(jù)證書的表在上線前已經(jīng)全量進(jìn)行major compaction操作,并且將數(shù)字證書的表移動到了重點業(yè)務(wù)組,這個問題也可以排除。
那到底是哪里的問題?為了找到問題的根源,必須找到超時的現(xiàn)場。于是使用Thriftclient模擬真實的業(yè)務(wù)訪問,并持續(xù)在ThriftServer上觀察日志。

通過上圖發(fā)現(xiàn),在訪問不同的ID的時候,發(fā)現(xiàn)有部分請求會超過3秒,并且在出現(xiàn)超過3秒的請求的時候,會出現(xiàn)如下的日志:

很明顯,上面顯示數(shù)字證書元數(shù)據(jù)從本地緩存中移走,路由發(fā)生了變動后,導(dǎo)致訪問超時了。因為HBase在路由發(fā)生變動后,Thrift Server緩存的元數(shù)據(jù)信息并不會主動的改變,而是在訪問出錯的時候,才會去重新獲取對應(yīng)region的元數(shù)據(jù)信息,因此導(dǎo)致了超時。路由變動是由于做優(yōu)化的時候?qū)?shù)字證書的表從普通組遷移到重點組中,從而導(dǎo)致路由發(fā)生了變化。
解決辦法:
既然訪問超時是由于路由緩存是錯誤的,那么重啟Thrift Server既可解決。
重啟Thrift Server后,第一次訪問是1秒多一點,然后對應(yīng)region的第二次以后的訪問都在50ms以內(nèi)了。第一次訪問耗時1秒多是因為集群中表的region太多,已經(jīng)超過了30萬個region。
region的尋址掃盲
為了讓大家更了解此次的問題,把之前概括的region的尋址方式也描述一下:
HBase的region尋址有老的尋址方式和新的尋址方式,先來看老的尋址方式:
老的尋址方式
在Hbase 0.96版本以前,Hbase有兩個特殊的表,分別是-ROOT-表和.META.表,其中-ROOT-的位置存儲在ZooKeeper中,-ROOT-本身存儲了 .META. Table的RegionInfo信息,并且-ROOT-不會分裂,只有一個region。而.META.表可以被切分成多個region。讀取的流程如下圖所示:

第1步:client請求ZK獲得-ROOT-所在的RegionServer地址
第2步:client請求-ROOT-所在的RS地址,獲取.META.表的地址,client會將-ROOT-的相關(guān)信息cache下來,以便下一次快速訪問
第3步:client請求 .META.表的RS地址,獲取訪問數(shù)據(jù)所在RegionServer的地址,client會將.META.的相關(guān)信息cache下來,以便下一次快速訪問
第4步:client請求訪問數(shù)據(jù)所在RegionServer的地址,獲取對應(yīng)的數(shù)據(jù)
從上面的路徑我們可以看出,用戶需要3次請求才能直到用戶Table真正的位置,這在一定程序帶來了性能的下降。在0.96之前使用3層設(shè)計的主要原因是考慮到元數(shù)據(jù)可能需要很大。但是真正集群運(yùn)行,元數(shù)據(jù)的大小其實很容易計算出來。在BigTable的論文中,每行METADATA數(shù)據(jù)存儲大小為1KB左右,如果按照一個Region為128M的計算,3層設(shè)計可以支持的Region個數(shù)為2^34個,采用2層設(shè)計可以支持2^17(131072)。那么2層設(shè)計的情況下一個 集群可以存儲4P的數(shù)據(jù)。這僅僅是一個Region只有128M的情況下。如果是10G呢? 因此,通過計算,其實2層設(shè)計就可以滿足集群的需求。因此在0.96版本以后就去掉了-ROOT-表了。
新的Region尋址方式
如上面的計算,2層結(jié)構(gòu)其實完全能滿足業(yè)務(wù)的需求,因此0.96版本以后將-ROOT-表去掉了。如下圖所示:

訪問路徑變成了3步:
第1步:Client請求ZK獲取.META.所在的RegionServer的地址。
第2步:Client請求.META.所在的RegionServer獲取訪問數(shù)據(jù)所在的RegionServer地址,client會將.META.的相關(guān)信息cache下來,以便下一次快速訪問。
第3步:Client請求數(shù)據(jù)所在的RegionServer,獲取所需要的數(shù)據(jù)。
需要注意的是Client的元數(shù)據(jù)緩存不更新,當(dāng).META.的數(shù)據(jù)發(fā)生更新。如上面的例子,由于Region1的位置發(fā)生了變化,Client再次根據(jù)緩存去訪問的時候,會出現(xiàn)錯誤,當(dāng)出現(xiàn)異常達(dá)到重試次數(shù)后就會去.META.所在的RegionServer獲取最新的數(shù)據(jù),如果.META.所在的RegionServer也變了,Client就會去ZK上獲取.META.所在的RegionServer的最新地址。
了解了Region的尋址方式以及了解了Client的緩存更新機(jī)制后,就能很好地了解這次問題的所在了。這次問題由于路由失效,導(dǎo)致Client去訪問失效的路由,訪問失敗以后,才重新去獲取Region的路由,獲取到正確的路由,自己緩存一份并且去新的路由訪問數(shù)據(jù)。而集群中Region太多導(dǎo)致路由尋址也很耗費(fèi)事件,這么長的路徑導(dǎo)致了請求超過了3秒。再加上業(yè)務(wù)側(cè)沒有對應(yīng)重試機(jī)制,因此導(dǎo)致大部分的請求都出現(xiàn)失敗。
后續(xù)的優(yōu)化措施可以從如下兩個維度來改進(jìn):
1、清理掉一些過期的表,減少region的數(shù)量;
2、業(yè)務(wù)側(cè)必須添加重試機(jī)制;