一、前言
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: 比较常用 , 应用灵活,采用快照技术,效率比较高
具体应用时,要结合自身表的特性,考虑数据规模、数据读写方式、实时数据&离线数据等方面,再选择使用哪种。