文章目录
一、最终流程
数据量预估,预分裂 ——> 准备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