1. 现有的三方包不能完全支持
- 官方:hbase-spark,不能设置 timestamp
- unicredit/hbase-rdd:接口太复杂,不能同时支持多个 family

2. HFile 得是有序的,排序依据 KeyValue.KVComparator,于是我们自定义一个 Comparator,内部调用 KeyValue.KVComparator

3. 如果没有自定义 partitioner,极有可能出现以下异常
ERROR: "java.io.IOException: Retry attempted 10 times without completing, bailing out"
https://community.hortonworks.com/content/supportkb/150138/error-javaioioexception-retry-attempted-10-times-w.html

自定义的方法,参考了:https://github.com/unicredit/hbase-rdd/blob/master/src/main/scala/unicredit/spark/hbase/HFileSupport.scala

4. 很多博客中有以下代码,一开始理解为可以用来对 rdd 分区,实际没有用。这是 mapreduce 的 job 参数,spark中不生效
val job = Job.getInstance(hbaseConfig)
HFileOutputFormat2.configureIncrementalLoad(job, table.getTableDescriptor, regionLocator)
job.getConfiguration

其他知识点:
1. scala 中实现 serializable 接口
2. HFilePartitioner,使用 hbase 的 regionLocator.getStartKeys,将 rdd 中的 put,按 rowkey 分割成不同的 partition,每个 partition 会产生一个 hfile,对应于 hbase region 的分区

代码,以后整理:

object BulkloadHelper {
private val logger = Logger.getLogger(this.getClass) def bulkloadWrite(rdd: RDD[Put], hbaseConfig: Configuration, thisTableName: TableName): Unit = {
val hbaseConnection = ConnectionFactory.createConnection(hbaseConfig)
val regionLocator = hbaseConnection.getRegionLocator(thisTableName)
val myPartitioner = HFilePartitioner.apply(hbaseConfig, regionLocator.getStartKeys, 1) logger.info(s"regionLocator.getStartKeys.length = ${regionLocator.getStartKeys.length}")
regionLocator.getStartKeys.foreach(keys => logger.info("regionLocator.getStartKeys: " + new String(keys))) val hFilePath = getHFilePath()
logger.info(s"bulkload, begin to write to hdfs path: $hFilePath") /**
* HFile sort function -> KeyValue.KVComparator
* CellComparator
*/
rdd.flatMap(put => putToKeyValueList(put))
.map(c => (c, 1))
.repartitionAndSortWithinPartitions(myPartitioner) // repartition so each hfile can match the hbase region
.map(tuple => (new ImmutableBytesWritable(tuple._1.row), tuple._1.getKeyValue()))
.saveAsNewAPIHadoopFile(
hFilePath,
classOf[ImmutableBytesWritable],
classOf[KeyValue],
classOf[HFileOutputFormat2],
hbaseConfig) // Bulk load Hfiles to Hbase
logger.info("bulkload, begin to load to hbase")
val bulkLoader = new LoadIncrementalHFiles(hbaseConfig)
bulkLoader.doBulkLoad(new Path(hFilePath), new HTable(hbaseConfig, thisTableName)) logger.info("bulkload, delete hdfs path")
val hadoopConf = new Configuration()
val fileSystem = FileSystem.get(hadoopConf)
fileSystem.delete(new Path(hFilePath), true)
hbaseConnection.close()
fileSystem.close()
logger.info("bulkload, done")
} def getHFilePath():String = "hdfs:///user/hadoop/hbase/bulkload/hfile/" + LocalDate.now().toString + "-" + UUID.randomUUID().toString /**
* select one keyvalue from put
* @param put
*/
def putToKeyValueList(put: Put): Seq[MyKeyValue] = {
put.getFamilyCellMap.asScala
.flatMap(_._2.asScala) // list cells
.map(cell => new MyKeyValue(put.getRow, cell.getFamily, cell.getQualifier, cell.getTimestamp, cell.getValue))
.toSeq
}
}

  

class MyKeyValue(var row: Array[Byte], var family: Array[Byte], var qualifier: Array[Byte], var timestamp: Long, var value: Array[Byte])
extends Serializable with Ordered[MyKeyValue] { import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream var keyValue: KeyValue = _ def getKeyValue(): KeyValue = {
if (keyValue == null) {
keyValue = new KeyValue(row, family, qualifier, timestamp, value)
}
keyValue
} @throws[IOException]
private def writeObject(out: ObjectOutputStream) {
keyValue = null
out.defaultWriteObject()
out.writeObject(this)
} @throws[IOException]
@throws[ClassNotFoundException]
private def readObject(in: ObjectInputStream) {
in.defaultReadObject()
val newKeyValue = in.readObject().asInstanceOf[MyKeyValue]
this.row = newKeyValue.row
this.family = newKeyValue.family
this.qualifier = newKeyValue.qualifier
this.timestamp = newKeyValue.timestamp
this.value = newKeyValue.value
getKeyValue()
} class MyComparator extends KeyValue.KVComparator with Serializable {}
val comparator = new MyComparator() override def compare(that: MyKeyValue): Int = {
comparator.compare(this.getKeyValue(), that.getKeyValue())
} override def toString: String = {
getKeyValue().toString
}
}

  

object HFilePartitionerHelper {
object HFilePartitioner {
def apply(conf: Configuration, splits: Array[Array[Byte]], numFilesPerRegionPerFamily: Int): HFilePartitioner = {
if (numFilesPerRegionPerFamily == 1)
new SingleHFilePartitioner(splits)
else {
val fraction = 1 max numFilesPerRegionPerFamily min conf.getInt(LoadIncrementalHFiles.MAX_FILES_PER_REGION_PER_FAMILY, 32)
new MultiHFilePartitioner(splits, fraction)
}
}
} protected abstract class HFilePartitioner extends Partitioner {
def extractKey(n: Any): Array[Byte] = {
// println(s"n = $n")
n match {
case kv: MyKeyValue => kv.row
}
}
} private class MultiHFilePartitioner(splits: Array[Array[Byte]], fraction: Int) extends HFilePartitioner {
override def getPartition(key: Any): Int = {
val k = extractKey(key)
val h = (k.hashCode() & Int.MaxValue) % fraction
for (i <- 1 until splits.length)
if (Bytes.compareTo(k, splits(i)) < 0) return (i - 1) * fraction + h (splits.length - 1) * fraction + h
} override def numPartitions: Int = splits.length * fraction
} private class SingleHFilePartitioner(splits: Array[Array[Byte]]) extends HFilePartitioner {
override def getPartition(key: Any): Int = {
val p = selfGetPartition(key)
// println(s"p = $p")
p
} def selfGetPartition(key: Any): Int = {
val k = extractKey(key)
for (i <- 1 until splits.length)
if (Bytes.compareTo(k, splits(i)) < 0) return i - 1 splits.length - 1
} override def numPartitions: Int = splits.length
}
}

  

spark bulkload hbase笔记的更多相关文章

  1. Spark、BulkLoad Hbase、单列、多列

    背景 之前的博客:Spark:DataFrame写HFile (Hbase)一个列族.一个列扩展一个列族.多个列 用spark 1.6.0 和 hbase 1.2.0 版本实现过spark BulkL ...

  2. Spark操作HBase问题:java.io.IOException: Non-increasing Bloom keys

    1 问题描述 在使用Spark BulkLoad数据到HBase时遇到以下问题: 17/05/19 14:47:26 WARN scheduler.TaskSetManager: Lost task ...

  3. MapReduce和Spark写入Hbase多表总结

    作者:Syn良子 出处:http://www.cnblogs.com/cssdongl 转载请注明出处 大家都知道用mapreduce或者spark写入已知的hbase中的表时,直接在mapreduc ...

  4. spark 操作hbase

    HBase经过七年发展,终于在今年2月底,发布了 1.0.0 版本.这个版本提供了一些让人激动的功能,并且,在不牺牲稳定性的前提下,引入了新的API.虽然 1.0.0 兼容旧版本的 API,不过还是应 ...

  5. Spark操作hbase

    于Spark它是一个计算框架,于Spark环境,不仅支持单个文件操作,HDFS档,同时也可以使用Spark对Hbase操作. 从企业的数据源HBase取出.这涉及阅读hbase数据,在本文中尽快为了尽 ...

  6. 大数据学习系列之九---- Hive整合Spark和HBase以及相关测试

    前言 在之前的大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 中介绍了集群的环境搭建,但是在使用hive进行数据查询的时候会非常的慢,因为h ...

  7. Spark 基本函数学习笔记一

      Spark 基本函数学习笔记一¶ spark的函数主要分两类,Transformations和Actions. Transformations为一些数据转换类函数,actions为一些行动类函数: ...

  8. Spark读Hbase优化 --手动划分region提高并行数

    一. Hbase的region 我们先简单介绍下Hbase的架构和Hbase的region: 从物理集群的角度看,Hbase集群中,由一个Hmaster管理多个HRegionServer,其中每个HR ...

  9. spark读写hbase性能对比

    一.spark写入hbase hbase client以put方式封装数据,并支持逐条或批量插入.spark中内置saveAsHadoopDataset和saveAsNewAPIHadoopDatas ...

随机推荐

  1. Spring Boot 如何动态切换数据源

    本章是一个完整的 Spring Boot 动态数据源切换示例,例如主数据库使用 lionsea 从数据库 lionsea_slave1.lionsea_slave2.只需要在对应的代码上使用 Data ...

  2. Hyperledger Explorer

    简介 Hyperledger Explorer is a simple, powerful, easy-to-use, well maintained, open source utility to ...

  3. 吴裕雄 python 人工智能——基于Mask_RCNN目标检测(3)

    import os import sys import random import math import re import time import numpy as np import cv2 i ...

  4. 第一阶段集训(这篇先写写tarjan以及圆方树)

    第一阶段的集训结束了w,不得不说oi太长时间不整是会退步的. 怎么说好呢,集训这几天过的很充实,知识收货很多,题调的也不少,自己的目标更明确了吧,不过这几天集训也是可以看出蒟蒻就是蒟蒻,还是太菜了.. ...

  5. 【PAT甲级】1094 The Largest Generation (25 分)(DFS)

    题意: 输入两个正整数N和M(N<100,M<N),表示结点数量和有孩子结点的结点数量,输出拥有结点最多的层的结点数量和层号(根节点为01,层数为1,层号向下递增). AAAAAccept ...

  6. 第八届极客大挑战 Re

    0x01.Writeup-RE-CM_2 题目: 解题思路: 1.这个是经过xor的,王老师提示说用xortool,于是放进kali,装好之后执行 xortool CM_2.exe -b, 0.out ...

  7. MQTT 入门介绍

    一.简述 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级& ...

  8. Steam 游戏 《Sudoku Universe(数独宇宙)》——[数独基本局分析]

    日期:2020.02.12 博客期:152 星期三 老师给的任务都做完了,15篇博客也都写好了,剩下的几天居然还要每天写一篇~唉~为难我 PH ,剩下的几天就把 我的数独要义分享一下吧! 1.基本局规 ...

  9. 【代码审计】MenInfo文件包含漏洞

    代码审计是我之前一直不敢涉及的领域,它包含的知识面太广.最近才开始学习,前段时间写的第一篇代码审计的文章 更多的感觉像是一个黑盒测试.我也会尽量把文章写的通俗易懂.代码审计 0基础从大牛的蜕变 从这里 ...

  10. 给服务添加路由--ingress

    kind: NamespaceapiVersion: v1metadata:  name: demo-webshell  generateName: demo-webshell  labels:    ...