1. BlukLoad 定义:
  1. 它是一种Hbase的批处理方式,可以提高效率,可作为优化的一部分。 在实际开发中,我们可能处理的数据量比较大,利用普通的Put来想Hbase中插入数据会降低程序的运行效率,所以Hbase为我们提供了批处理,向Hbase批量写入数据提高效率,在Hbase交互式命令行中,Hbase也提供了将数据批量插入到Hbase数据库中,命令行的批量插入原理就是先将文件转换成HFile文件,然后再将HFile文件导入到Hbase数据库中,那么利用API的方式的原理跟命令行的原理是相同的,以下就是利用API实现Spark处理后的数据批量导入到Hbase中。
  1. BlukLoad 优点:
  1. 消除对Hbase集群的写操作的压力,因为导入过程中不占用RegionServer的资源
  2. 能够快速导入海量数据
  3. 节省内存
  1. BlukLoad 原理:
  1. 利用HBase数据按照HFile格式存储在HDFS的原理,
  2. 使用Mapreduce直接生成HFile格式文件后,RegionServers再将HFile文件移动到相应的Region目录下
  1. 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)
  }
}
  1. 多列,在这里有一个比较重要的隐式转换来增强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)))
  }
}
  1. 命令行终端方式
  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
  1. 最常用的方法,而且能够大批量的迁徙数据,必会的
  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
  1. 将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
  1. 注意:当我们在向Hbase迁徙大量数据的时候,在创建表的时候需要创建多个Region,因为默认创建一张表值创建一个Region,那么当我们向表中插入大量数据的时候,由于只有一个Region,而Region是被RegionServer管理的,由此会增加单个服务器的内存压力,有可能宕机。所以我们在创建表的时候应该多创建Region,根据预估rowkey来创建。