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