Bulk Load——Spark 批量导入多列数据到HBase(scala/Java)

本文介绍了使用Spark批量导入HBase的流程,包括数据预处理、Put写入与BulkLoad的优势。通过Java和Scala尝试多列KeyValue的BulkLoad,探讨了预分区、优化策略与遇到的错误及其解决方案。最终实现了Spark的BulkLoad优化,提升了数据导入效率。
摘要由CSDN通过智能技术生成

一、最终流程

数据量预估,预分裂 ——> 准备HBase表 ——> Spark加载HDFS上的数据 ——> 数据清洗及排序 ——> 数据以HFile的形式写入HDFS ——> BulkLoad ——> 优化

未优化时,大概1200万条数据/h (10G数据)

二、使用Put写入

参考了很多资料后,猜测使用Put写入,也是Bulk Load,并不只是KeyValue才能批量加载
参考连接
只是Put是以行为单位,KeyValue以列为单位,按理说,应该是KeyValue更快
补充:该问题与我想法相似,追溯源码,到底层仍未看出究竟
在这里插入图片描述

import util.HdfsUtils
import org.apache.log4j.Logger
import org.slf4j.LoggerFactory
import org.apache.hadoop.hbase.client.{
   Put, Result}
import org.apache.hadoop.hbase.io.ImmutableBytesWritable
import org.apache.hadoop.hbase.util.Bytes
import org.apache.hadoop.hbase.mapreduce.TableOutputFormat
import org.apache.hadoop.mapreduce.Job
import org.apache.log4j.Level
import org.apache.spark.{
   SparkConf, SparkContext}

import scala.util.matching.Regex

/**
  * write data to HBase by 'Put'
  * Date: 2019-04-29
  * Time: 17:13
  * Author: wh
  * Version: V1.0.0
  */
class HBaseTest {
   

}

object CleanToHBase {
   
  private val PARTTERN: Regex = """。。。。。。""".r
  private val LOG = LoggerFactory.getLogger(classOf[HBaseTest])
  private val HdfsFilePath = HdfsUtils.HDFS_SCHEME + "。。。。。。"
  private final val NULL_FIELDS = Array("-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-")
  private val NUM_FIELDS: Int = 17


  /**
    * 解析输入的日志数据
    *
    * @param line logline
    * @return
    */
  def logLineSplit(line: String): Array[String] = {
   
    val options = PARTTERN.findFirstMatchIn(line)
    var fileds = new Array[String](NUM_FIELDS)
    // 。。。。。。清洗逻辑
    fileds
  }

  def main(args: Array[String]): Unit = {
   
    Logger.getLogger("org.apache.hadoop").setLevel(Level.WARN)
    Logger.getLogger("org.apache.spark").setLevel(Level.WARN)

    LOG.info("Start.")
    val startTime: Long = System.currentTimeMillis()

    // 1. Spark清洗
    val sparkConf = new SparkConf().setAppName("Put to HBase test").setMaster("local")
    val sc = new SparkContext(sparkConf)
    var logRDD = sc.textFile(HdfsFilePath, 12)
    val splitRDD = logRDD.map(line => logLineSplit(line))


    // 2. HBase 信息
    val tableName = "bdTest2"
    val familyName = Bytes.toBytes("infos")

    // 3. HBase MapReduce Bulk Job
    sc.hadoopConfiguration.set("hbase.zookeeper.quorum", "cluster")
    sc.hadoopConfiguration.set("hbase.zookeeper.property.clientPort", "2181")
    sc.hadoopConfiguration.set(TableOutputFormat.OUTPUT_TABLE, tableName)

    val hbaseBulkJob = Job.getInstance(sc.hadoopConfiguration)
    hbaseBulkJob.setOutputKeyClass(classOf[ImmutableBytesWritable])
    hbaseBulkJob.setOutputValueClass(classOf[Result])
    hbaseBulkJob.setOutputFormatClass(classOf[TableOutputFormat[ImmutableBytesWritable]])

    var i = 0
    // 4. write data to HBase
    val hbasePuts = splitRDD.map{
    line =>
      val put = new Put(Bytes.toBytes("row-" + System.nanoTime())) // 测试用
      put.addColumn(familyName, Bytes.toBytes("column name"), Bytes.toBytes(line(1)))
      put.addColumn(familyName, Bytes.toBytes("column name"), Bytes.toBytes(line(2)))
      // 。。。。。。other column
      (new ImmutableBytesWritable(), put)
    }
    hbasePuts.saveAsNewAPIHadoopDataset(hbaseBulkJob.getConfiguration)
    LOG.info("Done.")
    LOG.info("Time elapsed {} seconds.", (System.currentTimeMillis() - startTime) / 1000)
    sc.stop()
  }
}

注意以下区别,网上很多人,set了class又在save的时候传class,想想就知道,肯定有多余的啊:
在这里插入图片描述

三、批量写入,BulkLoad

找了大量资料,看了国内外很多文章,几乎都是KeyValue一个Cell写来写去
批量写入的优势:

  • 数据可立即供HBase使用
  • 不使用预写日志(WAL),不会出现flush和split(未验证)
  • 更少的垃圾回收
  • The bulk load feature uses a MapReduce job to output table data in HBase’s internal data format, and then directly loads the generated StoreFiles into a running cluster. Using bulk load will use less CPU and network resources than simply using the HBase API.

BulkLoad操作则是在外部以MapReduce作业的方式写HFile格式的文件,然后放入HDFS,再通知“HBase”来管理这些数据**
参考1
参考2
参考3
参考4

一般过程包括:

  • 估计数据的总大小,并确定HBase中的最佳region数
  • 创建于的空表,预分裂,为避免冷热数据,考虑对行键加盐
  • 在Spark中使用简单的自定义分区程序来拆分RDD,以匹配目标region拆分
  • 使用Spark和标准Hadoop库生成HFile
  • 使用标准HBase命令行批量加载工具(或代码)将数据加载到HBase中

四、Java BulkLoad 多列KeyValue(未成功-not Cell)

package core;

/*
 * It's not work, for List to Cell, see {@code BulkLoadToHBase.scala}
 * Date: 2019-04-30
 * Time: 下午3:38
 * Author: wh
 * Version: V1.0.0
 */
import util.HdfsUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapred.TableOutputFormat;
import org.apache.hadoop.hbase.mapreduce.HFileOutputFormat2;
import org.apache.hadoop.hbase.tool.LoadIncrementalHFiles;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.fs.Path;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值