HBase数据迁移方案

HBase2.1.0数据迁移方案


业务要求:将数据从旧的集群HBase1.2迁移到HBase2.1.0中去。每个表的数据量级10TB左右

值得注意:HBase升级到Hbase2.x之后,他的数据修复工具独立出来作为 HBCK2 成立了一个单独的项目 ,并且这个项目并不是所有的hbase都支持,其中就包含我们正在使用的HBase2.1.0

说明:步骤一和步骤二的代码打包的jar包名为readhbase-1.0-SNAPSHOT.jar步骤三的代码打包的包名为:bulkload.jar 因为两套集群的hbase版本不统一,因此生成region信息的maven依赖和写入region信息的maven依赖需要更换

如果你的版本支持HBCK2命令,那么你自己你迁移了HFile之后还是使用HBCK2命令比较好,我的这个方法可以用但是麻烦。

步骤一、迁移HFile数据

温馨提示:迁移之前一定要disable 需要迁移的hbase表,不然可能会报错,最可气的是报错了你没发现,一套流程走下来发现hbase的数据不够,原因就是部分数据没有迁移过来

使用Hadoop自带的命令distcp命令进行hadoop层面的数据移动,具体命令如下:【我的参考

hadoop distcp \
-Dmapreduce.job.name=jobname \
-bandwidth 20 \
-m 20 \
hdfs://192.168.1.1:8020/habse/data/default/cdr201906 \
hdfs://192.168.2.1:8020/copy_data/cdr201906

-m为指定的map数量,默认为20个

-bandwith为带宽限制,防止一次同步过多的文件影响其他任务运行

-Dmapreduce.job.name为任务的名称【yarn的网页显示出来】

步骤二、迁移表的region

如果有hbck2命令可以使用该命令进行修复,但是正如我们前面所提到的,HBCK2命令不支持HBase2.1.0。不信你去查看【点击进入】那么我们只能另想思路了。

我的思路为复制分区+bulkload方案。由于复制来的数据已经是HFile了,我们就可以直接拿着这批数据入bulk到hbase,但是这样存在一个弊端,那就是只会形成一个region,一旦是这样,那么这个10TB左右的region要面临无尽的分裂,这样会大量消耗hbase的性能,于是就加上一个预分区在前面。先复制分区到新的机器,然后再复制数据。

我复制分区的方案为手动复制,也就是写java代码拷贝。代码如下

package load;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

public class MyRegionInfo implements Serializable {
    private static final long serialVersionUID = 1L;

    private String tableName;
    private List<String> columnFamilies;

    private byte[][] regions;

    public List<String> getColumnFamilies() {
        return columnFamilies;
    }

    public void setColumnFamilies(List<String> columnFamilies) {
        this.columnFamilies = columnFamilies;
    }

    public MyRegionInfo(String tableName, List<String> columnFamilys, byte[][] regions) {
        this.tableName = tableName;
        this.columnFamilies = columnFamilys;
        this.regions = regions;
    }

    public MyRegionInfo() {
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public byte[][] getRegions() {
        return regions;
    }

    public void setRegions(byte[][] regions) {
        this.regions = regions;
    }

    @Override
    public String toString() {
        return "MyRegionInfo{" +
                "tableName='" + tableName + '\'' +
                ", columnFamilies=" + columnFamilies.size()+columnFamilies.get(0) +
                ", regions=" + Arrays.toString(regions) +
                '}';
    }
}

package load;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.RegionLocator;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class GetRegionInfo {
    public static void main(String[] args) throws Exception{
        if(args.length <  2){
            System.out.println("--------------请输入两个参数---------------");
            System.out.println("|参数一:需要拷贝的元数据的表名            |");
            System.out.println("|参数二:保存的元数据的文件名              |");
            System.out.println("|------------------------------------------|");
        }

        Configuration configuration = HBaseConfiguration.create();
        Connection connection = ConnectionFactory.createConnection(configuration);

        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File(args[1])));

        RegionLocator regionLocator = connection.getRegionLocator(TableName.valueOf(args[0]));
        List<HRegionLocation> regionLocations = regionLocator.getAllRegionLocations();
        List<String> families = new ArrayList();
        Collection<HColumnDescriptor> realFamilies = connection.getAdmin().getTableDescriptor(TableName.valueOf(args[0])).getFamilies();
        for(HColumnDescriptor descriptor:realFamilies){
            System.out.println("列族:"+descriptor.getNameAsString());
            families.add(descriptor.getNameAsString());
        }

        byte[][] regionsInfo = new byte[regionLocations.size() -1][];
        int i = 0;
        for(HRegionLocation location:regionLocations){
            HRegionInfo regionInfo = location.getRegionInfo();
            byte[] startKey = regionInfo.getStartKey();
            if(startKey != null && Bytes.compareTo(startKey, HConstants.EMPTY_BYTE_ARRAY) != 0 ){
                regionsInfo[i] = startKey;
                i++;
                System.out.println(startKey);
            }
        }
        MyRegionInfo myRegionInfo = new MyRegionInfo(args[0], families, regionsInfo);
        outputStream.writeObject(myRegionInfo);
        outputStream.flush();
        outputStream.close();
        connection.close();

    }
}

这个jar包执行方法为:

[root@a01 data]# java -jar readhbase-1.0-SNAPSHOT.jar trp2017 region_trp2017
# 参数一:trp2017 表示表名
# 参数二:region_trp2017 表示保存的文件名,里面存储的为region信息

然后执行之后就得到了region_trp2017这个文件,这个文件保存的就是region信息。拿到了这个文件之后去新的集群执行,就可以自动创建预分区之后的表。

创建表的代码如下:

package load;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;

public class CreateTable {
    public static void main(String[] args) throws Exception{
        if(args.length <= 0){
            System.out.println("--------------请输入两个参数---------------");
            System.out.println("|参数一:保存的元数据的文件名              |");
            System.out.println("|------------------------------------------|");
        }
        Configuration configuration = HBaseConfiguration.create();
        Connection connection = ConnectionFactory.createConnection(configuration);
        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(new File(args[0])));
        MyRegionInfo myRegionInfo = (MyRegionInfo) inputStream.readObject();
        Admin admin = connection.getAdmin();
        HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(myRegionInfo.getTableName()));
        List<String> columnFamilies = myRegionInfo.getColumnFamilies();
        for(String families:columnFamilies){
            hTableDescriptor.addFamily(new HColumnDescriptor(Bytes.toBytes(families)));
        }
        System.out.println(myRegionInfo);
        admin.createTable(hTableDescriptor,myRegionInfo.getRegions());
        connection.close();

    }
}

执行方法如下:

[root@p01 ~]# java -jar readhbase-1.0-SNAPSHOT.jar region_trp2017
[root@p01 ~]# 
# |该步骤在新的机器上执行,他会自动创建表,其中参数 region_trp2017 为上一步骤生成的文件名。里面存储的为region信息

步骤三、bulkload数据

分区信息拷贝之后再导入,此时导入就不用再次进行分裂了,我的导入代码为:【这里为scala代码因为我要借助spark执行】

package load

import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.fs.{FileSystem, Path}
import org.apache.hadoop.hbase.client.{ConnectionFactory, HTable, Table}
import org.apache.hadoop.hbase.tool.LoadIncrementalHFiles
import org.apache.hadoop.hbase.{HBaseConfiguration, TableName}
import scala.collection.JavaConverters._

object StartBulkLoad {
  def main(args: Array[String]): Unit = {
    if (args.length < 2) {
      System.out.println("--------------请输入两个参数---------------")
      System.out.println("|参数一:hfile存在的位置                    |")
      System.out.println("|参数二:导入的表名                         |")
      System.out.println("|-------------------------------------------|")
    }
    val config = HBaseConfiguration.create()
    val conn = ConnectionFactory.createConnection(config)
    val fs = FileSystem.get(new Configuration)
    val hfiles = fs.listStatus(new Path(args(0)))
    val load = new LoadIncrementalHFiles(config)
    for (hfile <- hfiles){
      if(hfile.getPath.getName.length == 32){
        load.doBulkLoad(hfile.getPath, conn.getAdmin, conn.getTable(TableName.valueOf(args(1))),
          conn.getRegionLocator(TableName.valueOf(args(1))))
      }
    }
  }

}

我的执行命令为【请用client模式】:

[root@p01 ~]# spark-submit --class load.StartBulkLoad --deploy-mode client --master yarn bulkload.jar /copy_data/trp2017 trp2017

看好日志,如果发现有错误,重新bulkload数据

此时数据已经导入完成。查看一下,果然好多数据在这里插入图片描述
旧集群统计一下数据的多少

[root@p01 ~] hbase org.apache.hadoop.hbase.mapreduce.RowCounter 'tri2018'
# 得到结果ROWS=395502806

新集群统计一下数据的多少

[root@p01 ~] hbase org.apache.hadoop.hbase.mapreduce.RowCounter 'tri2018'
# 得到结果ROWS=395502806
  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天心有情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值