1. 程式人生 > >HBase跨叢集複製Snapshot失敗原因分析及解決

HBase跨叢集複製Snapshot失敗原因分析及解決

起因

HBase快照在跨叢集複製時,經常會出現由於/hbase/.tmp/data/xxx FileNotFoundException導致任務失敗 
現還原出錯場景,並分析錯誤原因,給出一些常用的解決方法 

  • 主要原因 
    在建立快照到跨叢集複製過程中,部分StoreFile的位置發生了變動,以至不能正常定址( 使用webhdfs的bug)

場景還原

源叢集:HBase 1.2.0-cdh5.10.0 
目標叢集:HBase 1.2.0-cdh5.12.1

1. 建立表mytable,2個region,以03為分割,一個列族info

put 6條資料 

put 'mytable','01','info:age','1' 
put 'mytable','02','info:age','2' 
put 'mytable','03','info:age','3' 
put 'mytable','04','info:age','1' 
put 'mytable','05','info:age','1' 
put 'mytable','06','info:age','1'

2. 建立快照mysnapshot,生成以下檔案

  • .snapshot 包含了快照資訊,即HBaseProtos.SnapshotDescription物件 
    name: "mysnapshot" 
    table: "mytable" 
    creation_time: 1533774121010 
    type: FLUSH 
    version: 2

  • data.manifest 
    包含了hbase表schema、attributes、column_families,即HBaseProtos.SnapshotDescription物件,重點的是store_files資訊

3. 修改資料

通過Put 修改一個Region的資料 

put 'mytable','04','info:age','4' 
put 'mytable','05','info:age','5' 
put 'mytable','06','info:age','6'

4. 進行flush,major_compat

模擬跨叢集複製過程中出現的大/小合併

 

還原出錯

控制檯提示,FileNotFoundException,任務失敗

 

原始碼剖析

1 ExportSnapshot執行復制前會先將.snapshot,data.manifest 複製到目標端 .hbase-snapshot/.tmp/mysnapshot下

2 解析data.manifest,按照storefile進行邏輯切片,map每次會讀入一個SnapshotFileInfo的資訊,只包含了HFileLink資訊,並沒有包括具體路徑 

3 map階段 
每讀入一個SnapshotFileInfo時,拼接出關於StoreFile可能出現的4個路徑,讀取時按照該順序查詢 

/datafs/data/default/mytable/c48642fecae3913e0d09ba236b014667/info/3c5e9ec890f04560a396040fa8b592a3 
/datafs/.tmp/data/default/mytable/c48642fecae3913e0d09ba236b014667/info/3c5e9ec890f04560a396040fa8b592a3 
/datafs/mobdir/data/default/mytable/c48642fecae3913e0d09ba236b014667/info/3c5e9ec890f04560a396040fa8b592a3 
/datafs/archive/data/default/mytable/c48642fecae3913e0d09ba236b014667/info/3c5e9ec890f04560a396040fa8b592a3

當map讀入資料時,呼叫ExportSnapshot.ExportMapper#openSourceFile 初始化InputStream的過程中 
通過呼叫FileLink.tryOpen()方法中,來確定StoreFile的真實路徑路徑(遍歷4個路徑,丟擲異常說明不存在,繼續找下一路徑)

在debug中發現,fs為org.apache.hadoop.hdfs.web.WebHdfsFileSystem物件 
遺憾的是,WebHdfsFileSystem呼叫getPos()時,不會丟擲異常,因此獲取到如下路徑(檔案實際存在於archive) 

/datafs/data/default/mytable/c48642fecae3913e0d09ba236b014667/info/3c5e9ec890f04560a396040fa8b592a3 

並將 該路徑設定為currentPath(下一次會用到,避免重複判定)
當InputStream.read(buffer)時,呼叫FileLink.read() 
 
由於初始化時,並沒有使用正確的路徑,因此 in.read()時,丟擲FileNotFoundException(第一次) 
繼續呼叫tryOpen().read()方法遍歷4個路徑,此時 currentPath為 data路徑跳過,使用下一個路徑(檔案仍在archive下) 

/datafs/.tmp/data/default/mytable/c48642fecae3913e0d09ba236b014667/info/3c5e9ec890f04560a396040fa8b592a3

read錯誤路徑,再次丟擲FileNotFoundException(第二次),此異常向上丟擲,task失敗,觀察map日誌,可看到 

紅線之下的FileNotFoundException,即為read()時,丟擲的兩次異常 
紅線之上File does not exist 為ExportSnapshot 系呼叫 getSourceFileStatus產生,可以觀察到在遍歷 data/.tmp/mobdir 後尋找到了正確路徑archive(未打印出)

解決思路

綜上:查詢StoreFile時只會查詢data、.tmp目錄,不會查詢archive目錄 
因此解決思路上,一是避免StoreFile出現在archive下,二是能正確獲取到archive路徑

避免StoreFile出現在archive

根據生產經驗,在資料大量寫入過程中,Region下不斷生成StoreFile,當StoreFile數量達到閾值時,觸發大/小合併 
被合併的StoreFile檔案移動到了archive檔案下,可使用以下幾個方法避免複製時大/小合併

  1. 對錶進行major_compact後再建快照
  2. 如果表可以接受一段時間的不可用,幾分鐘到幾十分鐘不等,可對錶進行disable後再操作
  3. 或者適當調大 hbase.hstore.compaction.Threadhold(表寫入不頻繁下)
  4. 根據業務情況,儘可能大的錯開資料寫入與複製的間隔(等待大/小合併自動完成)

避免使用webhdfs

使用hdfs時,可以正常的丟擲異常(未具體使用)

修復原始碼bug

使得在定址過程中,可正確讀到archive資料夾 
借鑑getSourceFileStatus(),在for中加一行 fs.getFileStatus(),遍歷時正常丟擲FileNotFoundException 

將ExportSnapshot抽出,重新組織HFileLink,FileLink,WALLink依賴 
打包成一個hadoop jar,避免影響其它功能