HBase數據遷移方案介紹
一、前言
HBase數據遷移是很常見的操作,目前業界主要的遷移方式主要分為以下幾類:
圖1.HBase數據遷移方案
從上面圖中可看出,目前的方案主要有四類,Hadoop層有一類,HBase層有三類。下面分別介紹一下。
二、Hadoop層數據遷移
2.1 方案介紹
Hadoop層的數據遷移主要用到DistCp(Distributed Copy), 官方描述是:DistCp(分布式拷貝)是用于大規模集群內部和集群之間拷貝的工具。它使用Map/Reduce實現文件分發,錯誤處理和恢復,以及報告生成。它把文件和目錄的列表作為map任務的輸入,每個任務會完成源列表中部分文件的拷貝。
我們知道MR程序適合用來處理大批量數據, 其拷貝本質過程是啟動一個MR作業,不過DisctCp只有map,沒有reducer。在拷貝時,由于要保證文件塊的有序性,轉換的最小粒度是一個文件,而不像其它MR作業一樣可以把文件拆分成多個塊啟動多個map并行處理。如果同時要拷貝多個文件,DisctCp會將文件分配給多個map,每個文件單獨一個map任務。我們可以在執行同步時指定-m參數來設定要跑的map數量,默認設置是20。如果是集群間的數據同步,還需要考慮帶寬問題,所以在跑任務時還需要設定 bandwitdh 參數,以防止一次同步過多的文件造成帶寬過高影響其它業務。同時,由于我們HBase集群一般是不會開MR調度的,所以這里還需要用到單獨的MR集群來作主備數據同步,即在跑任務時還需要指定mapreduce相關參數。
簡單的distcp參數形式如下:
- hadoop distcp hdfs://src-hadoop-address:9000/table_name hdfs://dst-hadoop-address:9000/table_name
如果是獨立的MR集群來執行distcp,因為數據量很大,一般是按region目錄粒度來傳輸,同時傳輸到目標集群時,我們先把文件傳到臨時目錄,最后再目的集群上load表,我們用到的形式如下:
- hadoop distcp \
- -Dmapreduce.job.name=distcphbase \
- -Dyarn.resourcemanager.webapp.address=mr-master-ip:8088 \
- -Dyarn.resourcemanager.resource-tracker.address=mr-master-dns:8093 \
- -Dyarn.resourcemanager.scheduler.address=mr-master-dns:8091 \
- -Dyarn.resourcemanager.address=mr-master-dns:8090 \
- -Dmapreduce.jobhistory.done-dir=/history/done/ \
- -Dmapreduce.jobhistory.intermediate-done-dir=/history/log/ \
- -Dfs.defaultFS=hdfs://hbase-fs/ \
- -Dfs.default.name=hdfs://hbase-fs/ \
- -bandwidth 20 \
- -m 20 \
- hdfs://src-hadoop-address:9000/region-hdfs-path \
- hdfs://dst-hadoop-address:9000/tmp/region-hdfs-path
在這個過程中,需要注意源端集群到目的端集群策略是通的,同時hadoop/hbase版本也要注意是否一致,如果版本不一致,最終load表時會報錯。
2.2 方案實施
遷移方法如下:
第一步,如果是遷移實時寫的表,最好是停止集群對表的寫入,遷移歷史表的話就不用了,此處舉例表名為test;
第二步, flush表, 打開HBase Shell客戶端,執行如下命令:
- hbase> flush 'test'
第三步,拷貝表文件到目的路徑,檢查源集群到目標集群策略、版本等,確認沒問題后,執行如上帶MR參數的命令
第四步, 檢查目標集群表是否存在,如果不存在需要創建與原集群相同的表結構
第五步,在目標集群上,Load表到線上,在官方Load是執行如下命令:
- hbase org.jruby.Main add_table.rb /hbase/data/default/test
對于我們來說,因我們先把文件同步到了臨時目錄,并不在原表目錄,所以我們采用的另一種形式的load,即以region的維度來Load數據到線上表,怎么做呢,這里用到的是org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles這個類,即以bulkload的形式來load數據。上面同步時我們將文件同步到了目的集群的/tmp/region-hdfs-path目錄,那么我們在Load時,可以用如下命令來Load region文件:
- hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles -Dhbase.mapreduce.bulkload.max.hfiles.perRegion.perFamily=1024 hdfs://dst-hadoop-address:9000/tmp/region-hdfs-path/region-name table_name
這里還用到一個參數hbase.mapreduce.bulkload.max.hfiles.perRegion.perFamily, 這個表示在bulkload過程中,每個region列族的HFile數的上限,這里我們是限定了1024,也可以指定更少,根據實際需求來定。
第六步,檢查表數據是否OK,看bulkload過程是否有報錯
在同步過程中,我們為加塊同步速度,還會開個多線程來并發同步文件,這個可根據實際數據量和文件數來決定是否需要使用并發同步。
三、HBase層數據遷移
3.1 copyTable方式
copyTable也是屬于HBase數據遷移的工具之一,以表級別進行數據遷移。copyTable的本質也是利用MapReduce進行同步的,與DistCp不同的時,它是利用MR去scan 原表的數據,然后把scan出來的數據寫入到目標集群的表。這種方式也有很多局限,如一個表數據量達到T級,同時又在讀寫的情況下,全量scan表無疑會對集群性能造成影響。
來看下copyTable的一些使用參數:
- Usage: CopyTable [general options] [--starttime=X] [--endtime=Y] [--new.name=NEW] [--peer.adr=ADR] <tablename>
- Options:
- rs.class hbase.regionserver.class of the peer cluster
- specify if different from current cluster
- rs.impl hbase.regionserver.impl of the peer cluster
- startrow the start row
- stoprow the stop row
- starttime beginning of the time range (unixtime in millis)
- without endtime means from starttime to forever
- endtime end of the time range. Ignored if no starttime specified.
- versions number of cell versions to copy
- new.name new table's name
- peer.adr Address of the peer cluster given in the format
- hbase.zookeeer.quorum:hbase.zookeeper.client.port:zookeeper.znode.parent
- families comma-separated list of families to copy
- To copy from cf1 to cf2, give sourceCfName:destCfName.
- To keep the same name, just give "cfName"
- all.cells also copy delete markers and deleted cells
- Args:
- tablename Name of the table to copy
- Examples:
- To copy 'TestTable' to a cluster that uses replication for a 1 hour window:
- $ bin/hbase org.apache.hadoop.hbase.mapreduce.CopyTable --starttime=1265875194289 --endtime=1265878794289 --peer.adr=server1,server2,server3:2181:/hbase --families=myOldCf:myNewCf,cf2,cf3 TestTable
- For performance consider the following general options:
- -Dhbase.client.scanner.caching=100
- -Dmapred.map.tasks.speculative.execution=false
從上面參數,可以看出,copyTable支持設定需要復制的表的時間范圍,cell的版本,也可以指定列簇,設定從集群的地址,起始/結束行鍵等。參數還是很靈活的。
copyTable支持如下幾個場景:
1、表深度拷貝:相當于一個快照,不過這個快照是包含原表實際數據的,0.94.x版本之前是不支持snapshot快照命令的,所以用copyTable相當于可以實現對原表的拷貝, 使用方式如下:
- create 'table_snapshot',{NAME=>"i"}
- hbase org.apache.hadoop.hbase.mapreduce.CopyTable --new.name=tableCopy table_snapshot
2、集群間拷貝:在集群之間以表維度同步一個表數據,使用方式如下:
- create 'table_test',{NAME=>"i"} #目的集群上先創建一個與原表結構相同的表
- hbase org.apache.hadoop.hbase.mapreduce.CopyTable --peer.adr=zk-addr1,zk-addr2,zk-addr3:2181:/hbase table_test
3、增量備份:增量備份表數據,參數中支持timeRange,指定要備份的時間范圍,使用方式如下:
- hbase org.apache.hadoop.hbase.mapreduce.CopyTable ... --starttime=start_timestamp --endtime=end_timestamp
4、部分表備份:只備份其中某幾個列族數據,比如一個表有很多列族,但我只想備份其中幾個列族數據,CopyTable提供了families參數,同時還提供了copy列族到新列族形式,使用方式如下:
- hbase org.apache.hadoop.hbase.mapreduce.CopyTable ... --families=srcCf1,srcCf2 #copy cf1,cf2兩個列族,不改變列族名字
- hbase org.apache.hadoop.hbase.mapreduce.CopyTable ... --families=srcCf1:dstCf1, srcCf2:dstCf2 #copy srcCf1到目標dstCf1新列族
總的來說,CopyTable支持的范圍還是很多的,但因其涉及的是直接HBase層數據的拷貝,所以效率上會很低,同樣需要在使用過程中限定掃描原表的速度和傳輸的帶寬,這個工具實際上使用比較少,因為很難控制。
3.2 Export/Import方式
此方式與CopyTable類似,主要是將HBase表數據轉換成Sequence File并dump到HDFS,也涉及Scan表數據,與CopyTable相比,還多支持不同版本數據的拷貝,同時它拷貝時不是將HBase數據直接Put到目標集群表,而是先轉換成文件,把文件同步到目標集群后再通過Import到線上表。主要有兩個階段:
Export階段: 將原集群表數據Scan并轉換成Sequence File到Hdfs上,因Export也是依賴于MR的,如果用到獨立的MR集群的話,只要保證在MR集群上關于HBase的配置和原集群一樣且能和原集群策略打通(master®ionserver策略),就可直接用Export命令,如果沒有獨立MR集群,則只能在HBase集群上開MR,若需要同步多個版本數據,可以指定versions參數,否則默認同步最新版本的數據,還可以指定數據起始結束時間,使用如下:
- # output_hdfs_path可以直接是目標集群的hdfs路徑,也可以是原集群的HDFS路徑,如果需要指定版本號,起始結束時間
- hbase org.apache.hadoop.hbase.mapreduce.Export <tableName> <ouput_hdfs_path> <versions> <starttime> <endtime>
Import階段: 將原集群Export出的SequenceFile導到目標集群對應表,使用如下:
- #如果原數據是存在原集群HDFS,此處input_hdfs_path可以是原集群的HDFS路徑,如果原數據存在目標集群HDFS,則為目標集群的HDFS路徑
- hbase org.apache.hadoop.hbase.mapreduce.Import <tableName> <input_hdfs_path>
3.3 Snapshot方式
3.3.1 snapshot介紹
此方式與上面幾中方式有所區別,也是目前用得比較多的方案,snapshot字面意思即快照, 傳統關系型數據庫也有快照的概念,HBase中關于快照的概念定義如下:
快照就是一份元信息的合集,允許管理員恢復到表的先前狀態,快照不是表的復制而是一個文件名稱列表,因而不會復制數據
因不拷貝實際的數據,所以整個過程是比較快的,相當于對表當前元數據狀態作一個克隆,snapshot的流程主要有三個步驟:
圖2.數據遷移圖
加鎖: 加鎖對象是regionserver的memstore,目的是禁止在創建snapshot過程中對數據進行insert,update,delete操作
刷盤:刷盤是針對當前還在memstore中的數據刷到HDFS上,保證快照數據相對完整,此步也不是強制的,如果不刷會,快照中數據有不一致風險
創建指針: snapshot過程不拷貝數據,但會創建對HDFS文件的指針,snapshot中存儲的就是這些指標元數據
3.3.2 snapshot內部原理
snapshot實際內部是怎么做的呢,上面說到,snapshot只是對元數據信息克隆,不拷貝實際數據文件,我們以表test為例,這個表有三個region, 每個region分別有兩個HFile,創建snapshot過程如下:
圖3.snapshot創建內部原理
創建的snapshot放在目錄/hbase/.hbase-snapshot/下, 元數據信息放在/hbase/.hbase-snapshot/data.manifest中, 如上圖所示,snapshot中也分別包含對原表region HFile的引用,元數據信息具體包括哪哪些呢:
- 1. snapshot元數據信息
- 2. 表的元數據信息&schema,即原表的.tableinfo文件
- 3. 對原表Hfile的引用信息
由于我們表的數據在實時變化,涉及region的Hfile合并刪除等操作,對于snapshot而言,這部分數據HBase會怎么處理呢,實際上,當發現spit/compact等操作時,HBase會將原表發生變化的HFile拷貝到/hbase/.archive目錄,如上圖中如果Region3的F31&F32發生變化,則F31和F32會被同步到.archive目錄,這樣發生修改的文件數據不至于失效,如下圖所示:
圖4.snapshot文件遷移
快照中還有一個命令就是clone_snapshot, 這個命令也很用,我們可以用它來重命名表,恢復表數據等。具體用法如下:
- hbase> clone_snapshot 'snapshot_src_table' , 'new_table_name'
這個命令也是不涉及實際數據文件的拷貝,所以執行起來很快,那拷貝的是什么呢,與上面提到的引用文件不同,它所生成的是linkfile,這個文件不包含任何內容,和上面引用文件一樣的是,在發生compact等操作時,會將原文件copy到/hbase/.archive目錄。
比如我們有一個表test, 有一個region原表信息如下:
- hbaseuser:~> hadoop fs -ls /hbase/data/default/test/d8340c61f5d77345b7fa55e0dfa9b492/*
- Found 1 items
- -rw-r--r-- 1 hbaseuser supergroup 37 2017-12-01 11:44 /hbase/data/default/test/d8340c61f5d77345b7fa55e0dfa9b492/.regioninfo
- Found 1 items
- -rw-r--r-- 1 hbaseuser supergroup 983 2017-12-01 12:13 /hbase/data/default/test/d8340c61f5d77345b7fa55e0dfa9b492/i/55c5de40f58f4d07aed767c5d250191
在創建一個snapshot之后:snapshot 'test', 'snapshot_test',在/hbase/.hbase-snapshot目錄信息如下:
- hbaseuser~> hadoop fs -ls /hbase/.hbase-snapshot/snapshot_test
- Found 4 items
- -rw-r--r-- 1 hbaseuser supergroup 32 2017-12-01 12:13 /hbase/.hbase-snapshot/snapshot_test/.snapshotinfo
- drwxr-xr-x - hbaseuser supergroup 0 2017-12-01 12:13 /hbase/.hbase-snapshot/snapshot_test/.tabledesc
- drwxr-xr-x - hbaseuser supergroup 0 2017-12-01 12:13 /hbase/.hbase-snapshot/snapshot_test/.tmp
- drwxr-xr-x - hbaseuser supergroup 0 2017-12-01 12:13 /hbase/.hbase-snapshot/snapshot_test/d8340c61f5d77345b7fa55e0dfa9b492
在clone_snapshot之后:clone_snapshot 'snapshot_test','new_test',在/hbase/archive/data/default目錄,有對原表的link目錄,目錄名只是在原HFile的文件名基礎上加了個links-前綴,這樣我們可以通過這個來定位到原表的HFile,如下所示:
- hbaseuser:~> hadoop fs -ls /hbase/archive/data/default/test/d8340c61f5d77345b7fa55e0dfa9b492/i
- Found 1 items
- drwxr-xr-x - hbaseuser supergroup 0 2017-12-01 12:34 /hbase/archive/data/default/test/d8340c61f5d77345b7fa55e0dfa9b492/i/.links-55c5de40f58f4d07
此時,再執行合并操作:major_compact 'new_test',會發現/hbase/archive/data/default/目錄已經變成了實際表的數據文件,上面圖中/hbase/archive/data/default/test/d8340c61f5d77345b7fa55e0dfa9b492/i/.links-55c5de40f58f4d07這個已經不在了,取而代之的是如下所示文件:
- hbaseuser:~> hadoop fs -ls /hbase/archive/data/default/new_test/7e8636a768cd0c6141a3bb45b4098910/i
- Found 1 items
- -rw-r--r-- 1 hbaseuser supergroup 0 2017-12-01 12:48 /hbase/archive/data/default/new_test/7e8636a768cd0c6141a3bb45b4098910/i/test=d8340c61f5d77345b7fa55e0dfa9b492-55c5de40f58f4d07aed767c5d250191c
在實際的/hbase/data/default/new_test目錄也是實際的原表的數據文件,這樣完成了表數據的遷移。
3.3.3 snapshot數據遷移
snapshot的應用場景和上面CopyTable描述差不多,我們這里主要考慮的是數據遷移部分。數據遷移主要有以下幾個步驟:
A.創建快照:在原集群上,用snapshot命令創建快照,命令如下:
- hbase> snapshot 'src_table', 'snapshot_src_table'
- #查看創建的快照,可用list_snapshots命令
- hbase> list_snapshots
- #如果快照創建有問題,可以先刪除,用delete_snapshot命令
- hbase >delete_snapshot 'snapshot_src_table'
創建完快照后在/hbase根目錄會產生一個目錄:
- /hbase/.hbase-snapshot/snapshot_src_table
- #子目錄下有如下幾個文件
- /hbase/.hbase-snapshot/snapshot_src_table/.snapshotinfo
- /hbase/.hbase-snapshot/snapshot_src_table/data.manifest
B.數據遷移: 在上面創建好快照后,使用ExportSnapshot命令進行數據遷移,ExportSnapshot也是HDFS層的操作,本質還是利用MR進行遷移,這個過程主要涉及IO操作并消耗網絡帶寬,在遷移時要指定下map數和帶寬,不然容易造成機房其它業務問題,如果是單獨的MR集群,可以在MR集群上使用如下命令:
- hbase org.apache.hadoop.hbase.snapshot.ExportSnapshot \
- -snapshot snapshot_src_table \
- -copy-from hdfs://src-hbase-root-dir/hbase \
- -copy-to hdfs://dst-hbase-root-dir/hbase \
- -mappers 20 \
- -bandwidth 20
上面這些流程網上很多資料都有提到,對于我們業務來說,還有一種場景是要同步的表是正在實時寫的,雖然用上面的也可以解決,但考慮到我們表數據規模很大,幾十個T級別,同時又有實時業務在查的情況下,直接在原表上就算只是拷貝HFile,也會影響原集群機器性能,由于我們機器性能IO/內存方面本身就比較差,很容易導致機器異常,所以我們采用的其它一種方案,流程圖如下:
圖5.新的snapshot遷移方案
為什么要采用這種方案呢,主要考慮的是直接對原表snapshot進行Export會影響集群性能,所以采用折中的方案,即先把老表clone成一個新表,再對新表進行遷移,這樣可以避免直接對原表操作。
四、總結
上文把HBase數據遷移過程中常用的一些方法作了一個大概介紹,總結起來就四點:
DistCp: 文件層的數據同步,也是我們常用的
CopyTable: 這個涉及對原表數據Scan,然后直接Put到目標表,效率較低
Export/Import: 類似CopyTable, Scan出數據放到文件,再把文件傳輸到目標集群作Import
Snapshot: 比較常用 , 應用靈活,采用快照技術,效率比較高
具體應用時,要結合自身表的特性,考慮數據規模、數據讀寫方式、實時數據&離線數據等方面,再選擇使用哪種。