import org.apache.hadoop.hbase.mapreduce.TableOutputFormat
import org.apache.hadoop.hbase.mapreduce.TableInputFormat
import org.apache.hadoop.mapreduce.Job
import org.apache.hadoop.hbase.io.ImmutableBytesWritable
import org.apache.spark.rdd.PairRDDFunctions
def bulkWriteToHBase(sparkSession: SparkSession, sparkContext: SparkContext, jobContext: Map[String, String], sinkTableName: String, outRDD: RDD[(ImmutableBytesWritable, Put)]): Unit = {
val hConf = HBaseConfiguration.create()
hConf.set("hbase.zookeeper.quorum", jobContext("hbase.zookeeper.quorum"))
hConf.set("zookeeper.znode.parent", jobContext("zookeeper.znode.parent"))
hConf.set(TableInputFormat.INPUT_TABLE, sinkTableName)
val hJob = Job.getInstance(hConf)
hJob.getConfiguration().set(TableOutputFormat.OUTPUT_TABLE, sinkTableName)
hJob.setOutputFormatClass(classOf[TableOutputFormat[ImmutableBytesWritable]])
outRDD.saveAsNewAPIHadoopDataset(hJob.getConfiguration())
}
我通过使用这个hbase批量插入找到的是,每次spark只会从hbase写入一个区域服务器,这成为瓶颈。
然而,当我使用几乎相同的方法但是从hbase读取时,它使用多个执行器来进行并行读取。
def bulkReadFromHBase(sparkSession: SparkSession, sparkContext: SparkContext, jobContext: Map[String, String], sourceTableName: String) = {
val hConf = HBaseConfiguration.create()
hConf.set("hbase.zookeeper.quorum", jobContext("hbase.zookeeper.quorum"))
hConf.set("zookeeper.znode.parent", jobContext("zookeeper.znode.parent"))
hConf.set(TableInputFormat.INPUT_TABLE, sourceTableName)
val inputRDD = sparkContext.newAPIHadoopRDD(hConf, classOf[TableInputFormat], classOf[ImmutableBytesWritable], classOf[Result])
inputRDD
}
任何人都可以解释为什么会发生这种情况吗?或许我有 使用错误的方法进行spark-hbase批量I / O?
答案 0 :(得分:4)
问题:我使用了错误的方法进行spark-hbase批量I / O?
例如create 'test_table', 'f1', SPLITS=> ['1', '2', '3', '4', '5', '6', '7', '8', '9']
上表占据了9个地区..
设计好的rowkey,将以1-9
开头你可以像下面一样使用番石榴杂音哈希。
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
/**
* getMurmurHash.
*
* @param content
* @return HashCode
*/
public static HashCode getMurmurHash(String content) {
final HashFunction hf = Hashing.murmur3_128();
final HashCode hc = hf.newHasher().putString(content, Charsets.UTF_8).hash();
return hc;
}
final long hash = getMurmur128Hash(Bytes.toString(yourrowkey as string)).asLong();
final int prefix = Math.abs((int) hash % 9);
现在将此前缀附加到您的rowkey
例如
1rowkey1 //将进入第一个区域
2rowkey2 //将进入 第二区域 3rowkey3 //将进入第三区 ... 9rowkey9 // 将进入第九区域
如果您正在进行预拆分,并且想要手动管理区域拆分,您还可以通过将hbase.hregion.max.filesize设置为较大的数字并将拆分策略设置为ConstantSizeRegionSplitPolicy来禁用区域拆分。但是,您应该使用类似100GB的安全值,以便区域不会超出区域服务器的功能。您可以考虑禁用自动拆分并依赖预分割的初始区域集,例如,如果您使用统一哈希作为密钥前缀, 则可以确保每个区域都有读/写负载区域中的区域及其大小是统一的
1)请确保在将数据加载到hbase表之前可以预先对表进行预分割2)使用murmurhash或其他一些散列技术设计好的rowkey如下所述。确保各地区的分布均匀 另请查看http://hortonworks.com/blog/apache-hbase-region-splitting-and-merging/
问题:有人可以解释为什么会发生这种情况吗?
原因是非常明显和简单的 数据的热点,因为某个特定的原因,因为该表的链表不好 ......
考虑java中的hashmap,其中包含带有hashcode 1234的元素。然后它将填充一个桶中的所有元素isntit?如果hashmap元素分布在不同的商品hashcode
上,那么它会将元素放在不同的桶中。与hbase的情况相同。这里你的哈希码就像你的rowkey ...
如果我已经有一张桌子并且我想分割区域,会发生什么 跨...
RegionSplitter
类为选择手动拆分区域而不是自动处理HBase的开发人员提供了几个实用程序来帮助管理生命周期。
示例:
$ hbase org.apache.hadoop.hbase.util.RegionSplitter test_table HexStringSplit -c 10 -f f1
其中-c 10,将请求的区域数指定为10,-f指定表中所需的列族,以“:”分隔。该工具将创建一个名为“test_table”的表,其中包含10个区域:
13/01/18 18:49:32 DEBUG hbase.HRegionInfo: Current INFO from scan results = {NAME => 'test_table,,1358563771069.acc1ad1b7962564fc3a43e5907e8db33.', STARTKEY => '', ENDKEY => '19999999', ENCODED => acc1ad1b7962564fc3a43e5907e8db33,}
13/01/18 18:49:32 DEBUG hbase.HRegionInfo: Current INFO from scan results = {NAME => 'test_table,19999999,1358563771096.37ec12df6bd0078f5573565af415c91b.', STARTKEY => '19999999', ENDKEY => '33333332', ENCODED => 37ec12df6bd0078f5573565af415c91b,}
...
正如评论中所讨论的那样,你发现我写入hbase之前的最终RDD只有1个分区!这表明那里 只有一个执行者持有整个数据......我仍在努力 找出原因。
另外,检查
spark.default.parallelism
默认为所有核心的数量 机器。 parallelize api没有父RDD来确定 分区数,因此它使用spark.default.parallelism
。
因此,您可以通过重新分区来增加分区。
答案 1 :(得分:1)
虽然您没有提供示例数据或足够的解释,但这主要不是由于您的代码或配置。 由于非最佳的rowkey设计,它正在发生。 您正在编写的数据是键(hbase rowkey)结构不正确(可能单调增加或其他)。因此,写入其中一个区域正在发生。您可以防止这种情况发生。各种方式(各种推荐的rowkey设计实践,如盐渍,反转和其他技术)。 作为参考,您可以看到http://hbase.apache.org/book.html#rowkey.design
如果您想知道写入是针对所有区域并行进行还是逐个进行(不清楚问题)请查看: http://hbase.apache.org/book.html#_bulk_load