近期工作中使用hbase bulkload向hbase導(dǎo)入2TB數(shù)據(jù)(10000個(gè)hfiles),我們發(fā)現(xiàn)將hfiles加載到hbase 表的過程用了將近一個(gè)小時(shí)。這和我對(duì)bulk load過程的理解不太相符往扔,在我的理解中,hbase bulkload并不會(huì)產(chǎn)生數(shù)據(jù)copy熊户,數(shù)據(jù)導(dǎo)入通過hdfs的mv操作完成萍膛。那么,
問題1 :是什么操作消耗了一個(gè)小時(shí)呢嚷堡?
另外蝗罗,對(duì)bulkload一直有個(gè)疑問,
問題2 :基于某個(gè)hbase cluster中的表生成的hfiles能否導(dǎo)入到其他hbase cluster中的相同表(表名和列簇都相同)中蝌戒,bulkload會(huì)自動(dòng)處理兩個(gè)集群中表的region分布差異嗎绿饵?
帶著上述兩個(gè)問題閱讀了hbase中LoadIncrementalHFiles類的代碼,本文對(duì)此做個(gè)梳理瓶颠。
run方法
LoadIncrementalHFiles的main函數(shù)調(diào)用run方法:
run方法做了三件事:
1. 初始化
2. 判斷要導(dǎo)入的表是否存在拟赊,不存在且參數(shù)create.table為yes, 則創(chuàng)建該表;不存在且create.table不為yes則拋出異常TableNotFoundException
3. 調(diào)用doBulkLoad
初始化
LoadIncrementalHFiles的初始化過程比較簡(jiǎn)單粹淋,主要是對(duì)hbase admin等對(duì)象的初始化:
doBulkLoad
doBulkLoad方法執(zhí)行以下步驟:
1. 創(chuàng)建線程池
創(chuàng)建用于bulkload的線程池, 線程池大小由參數(shù)hbase.loadincremental.threads.max控制吸祟,默認(rèn)為當(dāng)前機(jī)器的core數(shù)量。源碼如下:
2. 初始化加載項(xiàng)隊(duì)列
遍歷指定目錄桃移,為每個(gè)hfile生成一個(gè)LoadQueueItem對(duì)象并添加到隊(duì)列中(下文我們稱此隊(duì)列為L(zhǎng)QI隊(duì)列屋匕,稱隊(duì)列中的元素為L(zhǎng)QI)中,該步驟由discoverLoadQueue方法完成借杰。
單個(gè)hfile的大小不應(yīng)超過HREGION_MAX_FILESIZE, 該值由參數(shù)hbase.hregion.max.filesize控制过吻,默認(rèn)為10GB。
一個(gè)LQI代表一個(gè)加載項(xiàng),LoadQueueItem類的源碼如下 :
需要加載的文件在HDFS上按照column family被分配在不同的子目錄下纤虽,每個(gè)子目錄下的一個(gè)文件就對(duì)應(yīng)一個(gè)LQI乳绕。
discoverLoadQueue方法中調(diào)用了visitBulkHFiles方法遍歷hfile所在的HDFS目錄,visitBulkHFiles方法對(duì)每個(gè)hfile會(huì)做一系列validation :
過濾掉reference逼纸,link, 以'_'開頭的洋措,以及非hfile格式的文件。
3. 檢查column family的有效性
在discoverLoadQueue完成對(duì)所有hfiles的遍歷后杰刽,會(huì)對(duì)queue中所有的items進(jìn)行column family的check菠发,如果存在某個(gè)item的column family不屬于目標(biāo)表,則拋出異常:
4. 循環(huán)分組加載
while循環(huán)的每次迭代主要執(zhí)行g(shù)roupOrSplit和bulkLoad兩個(gè)phase的操作:
a) groupOrSplitPhase
把queue中的所有文件根據(jù)目標(biāo)表的region metadata進(jìn)行分組贺嫂,把每個(gè)文件劃分到其所屬region滓鸠。
如果某個(gè)hfile的[firstkey, lastkey]不在任何region的[starkey, endkey]范圍內(nèi),則將此hfile拆分成兩個(gè)文件(拆分后的文件后綴為.top和.bottom)第喳,拆分的split key就是firstkey所在region的endkey哥力。
拆分后得到的兩個(gè)hfile會(huì)被封裝成LQI再添加回LQI隊(duì)列,這就是為什么需要一個(gè)while循環(huán)判斷LQI隊(duì)列是否為空墩弯。需注意,拆分后寞射,第一個(gè)LQI肯定會(huì)在某個(gè)region范圍內(nèi)(除非在下次迭代加載該LQI之前目標(biāo)region又發(fā)生了split)渔工,第二個(gè)LQI有可能仍需拆分。另外桥温,還要注意引矩,做完split之后,老的文件并不會(huì)刪除侵浸,所以bulkload過程結(jié)束后hfile的目錄下可能會(huì)有一些殘留文件旺韭,就是那些做了split之后留下的原始文件。
groupOrSplitPhase完成之后掏觉,所有可加載的LQI都會(huì)被放到regionGroups中区端。regionGroups是一個(gè)Multimap,key為region的startkey澳腹,value為對(duì)應(yīng)的LQI织盼,一個(gè)region可對(duì)應(yīng)多個(gè)LQI。
b) bulkLoadPhase :?
對(duì)于regionGroups中的每個(gè)key(即region的startkey)酱塔,調(diào)用方法tryAtomicRegionLoad將其對(duì)應(yīng)的所有LQI加載到目標(biāo)table中沥邻。如果加載失敗,則將failed LQI再加入到LQI隊(duì)列中羊娃,供下一循環(huán)檢測(cè)和加載唐全。tryAtomicRegionLoad方法會(huì)連接hbase region server,發(fā)送SecureBulkLoadHFilesRequest請(qǐng)求蕊玷。
groupOrSplit和bulkLoad的操作都是通過上面創(chuàng)建的線程池對(duì)所有hfile并發(fā)執(zhí)行的邮利。除了這兩個(gè)phase的操作外弥雹,while循環(huán)中還會(huì)檢測(cè)一些異常情況:
a) 對(duì)于doBulkLoad中while(!queue.isEmpty)循環(huán),如果經(jīng)過maxRetries次嘗試后近弟,LQI隊(duì)列仍不為空缅糟,則拋出異常。maxRetries由參數(shù)hbase.bulkload.retries.number控制祷愉,默認(rèn)為10 :
b) 經(jīng)過groupOrSplitPhase后窗宦,如果regionGroups中單個(gè)region單個(gè)column family對(duì)應(yīng)的hfile個(gè)數(shù)超過了maxFilesPerRegionPerFamily,則拋出異常:
maxFilesPerRegionPerFamily由參數(shù)hbase.mapreduce.bulkload.max.hfiles.perRegion.perFamily控制二鳄,默認(rèn)為32赴涵。
問題1解答
通過上面的分析,我們知道bulkload過程對(duì)hfile目錄進(jìn)行遍歷订讼,每個(gè)hfile都會(huì)進(jìn)行一系列validation髓窜,生成LQI,最終調(diào)用tryAtomicRegionLoad進(jìn)行加載欺殿。我們通過打印每個(gè)步驟的耗時(shí)寄纵,發(fā)現(xiàn)檢測(cè)hfile文件格式,即visitBulkHFiles中調(diào)用的isHFileFormat方法是主要的耗時(shí)步驟, 這是因?yàn)関isitBulkHFiles方法是在主線程串行執(zhí)行的脖苏,我們有10000個(gè)文件程拭,并且每次isHFileFormat都會(huì)讀取hfile的file trailer,所以累計(jì)時(shí)間很長(zhǎng)棍潘。
我們通過添加一個(gè)hbase配置項(xiàng)hbase.client.bulk.load.validate.hfile.format來控制是否進(jìn)行hfile格式檢測(cè)恃鞋,當(dāng)將其設(shè)置為false時(shí),加載2TB數(shù)據(jù)(10000個(gè)hfile)從之前的1個(gè)小時(shí)縮短為10分鐘亦歉。繞過文件格式檢查的前提是我們確定hfile的format都是正確的恤浪。我們還可以通過減少hfile的個(gè)數(shù)來減少bulkload在客戶端運(yùn)行的時(shí)間。還有一個(gè)可能的解決方案是將visitBulkHFiles方法修改成多線程執(zhí)行肴楷,以后有時(shí)間可以嘗試一下水由。
問題2解答
答案是肯定的。如上文所述赛蔫,bulkload會(huì)將hfile的[firstkey, lastkey]和目標(biāo)表region的[startkey, endkey]進(jìn)行匹配绷杜,如果匹配失敗則會(huì)進(jìn)行文件拆分,所以不用擔(dān)心不同集群表中region的差異濒募。
總結(jié)
本文對(duì)hbase bulkload的客戶端過程進(jìn)行了分析鞭盟,詳述了hfile的遍歷,檢測(cè)瑰剃,分組齿诉,拆分,加載等步驟,并對(duì)文中開頭提出的兩個(gè)問題進(jìn)行了解答粤剧。
水平有限歇竟,若有誤解,望讀者指正抵恋。
說明
hbase源碼版本:1.1.2