一、前言
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参数形式如下:
hadoopdistcphdfs://src-hadoop-address:9000/table_namehdfs://dst-hadoop-address:9000/table_name
如果是独立的MR集群来执行distcp,因为数据量很大,一般是按region目录粒度来传输,同时传输到目标集群时,我们先把文件传到临时目录,最后再目的集群上load表,我们用到的形式如下:
hadoopdistcp\ -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/\ -bandwidth20\ -m20\ 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是执行如下命令:
hbaseorg.jruby.Mainadd_table.rb/hbase/data/default/test
对于我们来说,因我们先把文件同步到了临时目录,并不在原表目录,所以我们采用的另一种形式的load,即以region的维度来Load数据到线上表,怎么做呢,这里用到的是org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles这个类,即以bulkload的形式来load数据。上面同步时我们将文件同步到了目的集群的/tmp/region-hdfs-path目录,那么我们在Load时,可以用如下命令来Load region文件:
hbaseorg.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles-Dhbase.mapreduce.bulkload.max.hfiles.perRegion.perFamily=1024hdfs://dst-hadoop-address:9000/tmp/region-hdfs-path/region-nametable_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[generaloptions][--starttime=X][--endtime=Y][--new.name=NEW][--peer.adr=ADR]<tablename> Options: rs.classhbase.regionserver.classofthepeercluster specifyifdifferentfromcurrentcluster rs.implhbase.regionserver.implofthepeercluster startrowthestartrow stoprowthestoprow starttimebeginningofthetimerange(unixtimeinmillis) withoutendtimemeansfromstarttimetoforever endtimeendofthetimerange.Ignoredifnostarttimespecified. versionsnumberofcellversionstocopy new.namenewtable'sname peer.adrAddressofthepeerclustergivenintheformat hbase.zookeeer.quorum:hbase.zookeeper.client.port:zookeeper.znode.parent familiescomma-separatedlistoffamiliestocopy Tocopyfromcf1tocf2,givesourceCfName:destCfName. Tokeepthesamename,justgive"cfName" all.cellsalsocopydeletemarkersanddeletedcells Args: tablenameNameofthetabletocopy Examples: Tocopy'TestTable'toaclusterthatusesreplicationfora1hourwindow: $bin/hbaseorg.apache.hadoop.hbase.mapreduce.CopyTable--starttime=1265875194289--endtime=1265878794289--peer.adr=server1,server2,server3:2181:/hbase--families=myOldCf:myNewCf,cf2,cf3TestTable Forperformanceconsiderthefollowinggeneraloptions: -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"} hbaseorg.apache.hadoop.hbase.mapreduce.CopyTable--new.name=tableCopytable_snapshot
2、集群间拷贝:在集群之间以表维度同步一个表数据,使用方式如下:
create'table_test',{NAME=>"i"}#目的集群上先创建一个与原表结构相同的表 hbaseorg.apache.hadoop.hbase.mapreduce.CopyTable--peer.adr=zk-addr1,zk-addr2,zk-addr3:2181:/hbasetable_test
3、增量备份:增量备份表数据,参数中支持timeRange,指定要备份的时间范围,使用方式如下:
hbaseorg.apache.hadoop.hbase.mapreduce.CopyTable...--starttime=start_timestamp--endtime=end_timestamp
4、部分表备份:只备份其中某几个列族数据,比如一个表有很多列族,但我只想备份其中几个列族数据,CopyTable提供了families参数,同时还提供了copy列族到新列族形式,使用方式如下:
hbaseorg.apache.hadoop.hbase.mapreduce.CopyTable...--families=srcCf1,srcCf2#copycf1,cf2两个列族,不改变列族名字 hbaseorg.apache.hadoop.hbase.mapreduce.CopyTable...--families=srcCf1:dstCf1,srcCf2:dstCf2#copysrcCf1到目标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路径,如果需要指定版本号,起始结束时间 hbaseorg.apache.hadoop.hbase.mapreduce.Export<tableName><ouput_hdfs_path><versions><starttime><endtime>
Import阶段: 将原集群Export出的SequenceFile导到目标集群对应表,使用如下:
#如果原数据是存在原集群HDFS,此处input_hdfs_path可以是原集群的HDFS路径,如果原数据存在目标集群HDFS,则为目标集群的HDFS路径 hbaseorg.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:~>hadoopfs-ls/hbase/data/default/test/d8340c61f5d77345b7fa55e0dfa9b492/* Found1items -rw-r--r--1hbaseusersupergroup372017-12-0111:44/hbase/data/default/test/d8340c61f5d77345b7fa55e0dfa9b492/.regioninfo Found1items -rw-r--r--1hbaseusersupergroup9832017-12-0112:13/hbase/data/default/test/d8340c61f5d77345b7fa55e0dfa9b492/i/55c5de40f58f4d07aed767c5d250191
在创建一个snapshot之后:snapshot ‘test’, ‘snapshot_test’,在/hbase/.hbase-snapshot目录信息如下:
hbaseuser~>hadoopfs-ls/hbase/.hbase-snapshot/snapshot_test Found4items -rw-r--r--1hbaseusersupergroup322017-12-0112:13/hbase/.hbase-snapshot/snapshot_test/.snapshotinfo drwxr-xr-x-hbaseusersupergroup02017-12-0112:13/hbase/.hbase-snapshot/snapshot_test/.tabledesc drwxr-xr-x-hbaseusersupergroup02017-12-0112:13/hbase/.hbase-snapshot/snapshot_test/.tmp drwxr-xr-x-hbaseusersupergroup02017-12-0112:13/hbase/.hbase-snapshot/snapshot_test/d8340c61f5d77345b7fa55e0dfa9b492
在clone_snapshot之后:clone_snapshot ‘snapshot_test’,’new_test’,在/hbase/archive/data/default目录,有对原表的link目录,目录名只是在原HFile的文件名基础上加了个links-前缀,这样我们可以通过这个来定位到原表的HFile,如下所示:
hbaseuser:~>hadoopfs-ls/hbase/archive/data/default/test/d8340c61f5d77345b7fa55e0dfa9b492/i Found1items drwxr-xr-x-hbaseusersupergroup02017-12-0112: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:~>hadoopfs-ls/hbase/archive/data/default/new_test/7e8636a768cd0c6141a3bb45b4098910/i Found1items -rw-r--r--1hbaseusersupergroup02017-12-0112: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集群上使用如下命令:
hbaseorg.apache.hadoop.hbase.snapshot.ExportSnapshot\ -snapshotsnapshot_src_table\ -copy-fromhdfs://src-hbase-root-dir/hbase\ -copy-tohdfs://dst-hbase-root-dir/hbase\ -mappers20\ -bandwidth20
上面这些流程网上很多资料都有提到,对于我们业务来说,还有一种场景是要同步的表是正在实时写的,虽然用上面的也可以解决,但考虑到我们表数据规模很大,几十个T级别,同时又有实时业务在查的情况下,直接在原表上就算只是拷贝HFile,也会影响原集群机器性能,由于我们机器性能IO/内存方面本身就比较差,很容易导致机器异常,所以我们采用的其它一种方案,流程图如下:
图5.新的snapshot迁移方案
为什么要采用这种方案呢,主要考虑的是直接对原表snapshot进行Export会影响集群性能,所以采用折中的方案,即先把老表clone成一个新表,再对新表进行迁移,这样可以避免直接对原表操作。
四、总结
上文把HBase数据迁移过程中常用的一些方法作了一个大概介绍,总结起来就四点:
DistCp: 文件层的数据同步,也是我们常用的
CopyTable: 这个涉及对原表数据Scan,然后直接Put到目标表,效率较低
Export/Import: 类似CopyTable, Scan出数据放到文件,再把文件传输到目标集群作Import
Snapshot: 比较常用 , 应用灵活,采用快照技术,效率比较高
具体应用时,要结合自身表的特性,考虑数据规模、数据读写方式、实时数据&离线数据等方面,再选择使用哪种。