Hbase之Spark通过BlukLoad的方式批量加载数据到HBase中

  1. BlukLoad 定义:

    1. 它是一种Hbase的批处理方式,可以提高效率,可作为优化的一部分。 在实际开发中,我们可能处理的数据量比较大,利用普通的Put来想Hbase中插入数据会降低程序的运行效率,所以Hbase为我们提供了批处理,向Hbase批量写入数据提高效率,在Hbase交互式命令行中,Hbase也提供了将数据批量插入到Hbase数据库中,命令行的批量插入原理就是先将文件转换成HFile文件,然后再将HFile文件导入到Hbase数据库中,那么利用API的方式的原理跟命令行的原理是相同的,以下就是利用API实现Spark处理后的数据批量导入到Hbase中。
  2. BlukLoad 优点:

    1. 消除对Hbase集群的写操作的压力,因为导入过程中不占用RegionServer的资源
    2. 能够快速导入海量数据
    3. 节省内存
  3. BlukLoad 原理:

    1. 利用HBase数据按照HFile格式存储在HDFS的原理,
    2. 使用Mapreduce直接生成HFile格式文件后,RegionServers再将HFile文件移动到相应的Region目录下
  4. BlukLoad 两种方式:

    1. 单列

      package com.lyz.hbase
      
      import org.apache.hadoop.fs.Path
      import org.apache.spark._
      import org.apache.hadoop.hbase.client.{ConnectionFactory, HBaseAdmin}
      import org.apache.hadoop.hbase.io.ImmutableBytesWritable
      import org.apache.hadoop.hbase.mapreduce._
      import org.apache.hadoop.hbase.util.Bytes
      import org.apache.hadoop.hbase.{HBaseConfiguration, KeyValue, TableName}
      import org.apache.hadoop.mapreduce.Job
      import org.apache.spark.sql.SparkSession
      
      object HbaseBatch {
        def main(args: Array[String]): Unit = {
          val spark = SparkSession.builder().appName("DataInput").master("local[2]").getOrCreate()
          val sc = spark.sparkContext
      
          val tableNameStr = "user"
      
          //获取配置文件的属性,为连接Hbase提供参数
          val configuraton = HBaseConfiguration.create
      
          //连接与Hbase建立连接
          val conn = ConnectionFactory.createConnection(configuraton)
      
          //获取Hbase的表的名字
          val tableName = TableName.valueOf(tableNameStr)
          //根据表的名字来得到表
          val table = conn.getTable(tableName)
      
          //设定HBase的输入表
          configuraton.set(TableInputFormat.INPUT_TABLE, tableNameStr)
      
          //初始化一个Job
          val job = Job.getInstance(configuraton)
      
          //设置输出的Key类型
          job.setMapOutputKeyClass(classOf[ImmutableBytesWritable])
      
          //设置输出值的类型
          job.setMapOutputValueClass(classOf[KeyValue])
      
          val regionLocator = conn.getRegionLocator(tableName)
          HFileOutputFormat2.configureIncrementalLoad(job, table, regionLocator)
      
          val array = Array(1 to 10: _*)
          val value = sc.parallelize(array)
          val rdd = value.map(x => {
            //KeyValue 是Hbase数据存储的单元
            val kv: KeyValue = new KeyValue(Bytes.toBytes(x), "info".getBytes(), "c1".getBytes(), "value_xxx".getBytes())
            (new ImmutableBytesWritable(Bytes.toBytes(x)), kv)
          })
      
      
          //数据转换成HFile,导入到HDFS目录中
          rdd.saveAsNewAPIHadoopFile("/hbase/data1", classOf[ImmutableBytesWritable], classOf[KeyValue], classOf[HFileOutputFormat2], configuraton)
      
      
          //将HDFS上的HFile导入到Hbase的表中
          val bulkLoader = new LoadIncrementalHFiles(configuraton)
          bulkLoader.doBulkLoad(new Path("/hbase/data1"), conn.getAdmin, table, regionLocator)
        }
      }
      
    2. 多列,在这里有一个比较重要的隐式转换来增强RDD的功能import org.apache.hadoop.hbase.spark.HBaseRDDFunctions._

      package com.lyz.hbase.domian
      
      /**
        * 类的注释
        *
        * @Package com.lyz.hbase.domian
        * @ClassName HbaseDomain
        * @Description ${TODO}
        * @Author liyuzhi
        * @Date 2018-07-04 9:35
        */
      case class HbaseDomain(id: String, name: String, age: Int)
      
      package com.lyz.hbase.HbaseService
      
      import java.util.UUID
      
      import com.lyz.hbase.domian.HbaseDomain
      import org.apache.avro.SchemaBuilder.ArrayBuilder
      import org.apache.hadoop.hbase.util.Bytes
      
      import scala.collection.mutable.ListBuffer
      
      object HbaseService {
      
        def getHbaseDomain: ListBuffer[HbaseDomain] = {
      
          val hbaseDomainList = new ListBuffer[HbaseDomain]
          for (i <- 1 to 10) {
            val uuid = UUID.randomUUID().toString
            val hbaseDomain = new HbaseDomain(uuid, "liyuzhi" + i, i)
            hbaseDomainList.append(hbaseDomain)
          }
          hbaseDomainList
        }
      
        def getHbaseDomainArray: ListBuffer[(Array[Byte], Array[(Array[Byte], Array[Byte], Array[Byte])])] = {
          val domains = getHbaseDomain
          val array = new ListBuffer[(Array[Byte], Array[(Array[Byte], Array[Byte], Array[Byte])])]
          domains.foreach(domain => {
            array.append((Bytes.toBytes(domain.id), Array((Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes(domain.name)))))
            array.append((Bytes.toBytes(domain.id), Array((Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes(domain.age)))))
          })
          array
        }
      }
      
      package com.lyz.hbase
      
      import org.apache.hadoop.fs.Path
      import org.apache.hadoop.hbase.client.ConnectionFactory
      import org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles
      import org.apache.hadoop.hbase.spark.HBaseRDDFunctions._
      import org.apache.hadoop.hbase.spark.{HBaseContext, KeyFamilyQualifier}
      import org.apache.hadoop.hbase.util.Bytes
      import org.apache.hadoop.hbase.{HBaseConfiguration, TableName}
      import org.apache.spark.SparkContext
      
      object HbaseBatch1 {
        def main(args: Array[String]): Unit = {
      
          val sc = new SparkContext("local", "test")
          //获取配置文件的属性,为连接Hbase提供参数
          val configuraton = HBaseConfiguration.create
          val hbaseContext = new HBaseContext(sc, configuraton)
      
          val tableNameStr = "user"
          val loadPathStr = "/hbase/data1"
      
          //连接与Hbase建立连接
          val conn = ConnectionFactory.createConnection(configuraton)
          //获取Hbase表
          val table = conn.getTable(TableName.valueOf(tableNameStr))
          val rdd = sc.parallelize(HbaseService.HbaseService.getHbaseDomainArray)
      
          rdd.hbaseBulkLoad(hbaseContext, TableName.valueOf(tableNameStr),
            t => {
              val rowKey = t._1
              val family: Array[Byte] = t._2(0)._1
              val qualifier = t._2(0)._2
              val value = t._2(0)._3
              val keyFamilyQualifier = new KeyFamilyQualifier(rowKey, family, qualifier)
              Seq((keyFamilyQualifier, value)).iterator
            },
            loadPathStr)
      
          val load = new LoadIncrementalHFiles(configuraton)
          load.doBulkLoad(new Path(loadPathStr), conn.getAdmin, table, conn.getRegionLocator(TableName.valueOf(tableNameStr)))
        }
      }
      
    3. 命令行终端方式

      1. (利用importtsv方法迁徙tsv数据,这个缺点是数据只保存在内存中,并没有保存磁盘中)

        HADOOP_CLASSPATH=`${HBASE_HOME}/bin/hbase classpath` \
        ${HADOOP_HOME}/bin/hadoop jar ${HBASE_HOME}/lib/hbase-server-1.2.0-cdh5.7.0.jar \
        importtsv  -Dimporttsv.columns=HBASE_ROW_KEY,info:name,info:age,info:address,info:phone \
        student hdfs://hadoop001:8020/opt/testfile/importtsv   
      2. 最常用的方法,而且能够大批量的迁徙数据,必会的

        1. 先将数据变成Hfile

          HADOOP_CLASSPATH=`${HBASE_HOME}/bin/hbase classpath` ${HADOOP_HOME}/bin/hadoop jar \
          ${HBASE_HOME}/lib/hbase-server-1.2.0-cdh5.7.0.jar importtsv  -Dimporttsv.columns=HBASE_ROW_KEY,info:name,info:age,info:address,info:phone \
          -Dimporttsv.bulk.output=hdfs://hadoop001:8020/opt/testfile/hfileoutput \
          student2 hdfs://hadoop001:8020/opt/testfile/importtsv
        2. 将Hfile同步到Hbase数据库里

          HADOOP_CLASSPATH=`${HBASE_HOME}/bin/hbase classpath` ${HADOOP_HOME}/bin/hadoop jar ${HBASE_HOME}/lib/hbase-server-1.2.0-cdh5.7.0.jar \
          completebulkload hdfs://hadoop001:8020/opt/testfile/hfileoutput student2
  5. 注意:当我们在向Hbase迁徙大量数据的时候,在创建表的时候需要创建多个Region,因为默认创建一张表值创建一个Region,那么当我们向表中插入大量数据的时候,由于只有一个Region,而Region是被RegionServer管理的,由此会增加单个服务器的内存压力,有可能宕机。所以我们在创建表的时候应该多创建Region,根据预估rowkey来创建。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值