Apache spark Spark仅写入一个hbase区域服务器

Apache spark Spark仅写入一个hbase区域服务器,apache-spark,hadoop,hbase,rdd,Apache Spark,Hadoop,Hbase,Rdd,通过使用此hbase大容量插入,我发现spark每次只能从hbase写入一个区域服务器,这成为瓶颈 然而,当我使用几乎相同的方法但从hbase读取时,它使用多个执行器来执行并行读取 import org.apache.hadoop.hbase.mapreduce.TableOutputFormat import org.apache.hadoop.hbase.mapreduce.TableInputFormat import org.apache.hadoop.mapreduce.Job imp

通过使用此hbase大容量插入,我发现spark每次只能从hbase写入一个区域服务器,这成为瓶颈

然而,当我使用几乎相同的方法但从hbase读取时,它使用多个执行器来执行并行读取

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())
}
谁能解释一下为什么会发生这种情况?也许我有 spark hbase批量I/O使用了错误的方式


虽然您没有提供示例数据或足够的解释,但这主要不是由于您的代码或配置造成的。 由于非最优的行键设计,这种情况正在发生。 您正在写入的数据的键(hbase rowkey)结构不正确(可能是单调递增或其他方式)。因此,正在写入其中一个区域。您可以通过各种方法(rowkey设计的各种推荐做法,如盐析、反转和其他技术)防止这种情况发生。 作为参考,你可以看到

在这种情况下,如果您想知道是对所有区域并行写入还是逐个写入(问题不清楚),请查看以下内容:

问题:我对spark hbase批量I/O使用了错误的方式

不,您的方法是正确的,不过,您需要在手动创建带有预裂区域的表之前预裂区域。 例如
创建'test_table','f1',SPLITS=>['1','2','3','4','5','6','7','8','9']

上表占据了9个地区

设计好的rowkey和will从1-9开始

您可以使用番石榴杂音哈希如下

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
}
现在将此前缀附加到行键

比如说

1rowkey1//将进入第一个区域
2rowkey2//将进入 第二区域
3rowkey3//将进入第三个区域 ... 9rowkey9// 将进入第九区域

如果正在执行预拆分,并且希望手动管理区域拆分,还可以通过将hbase.hregion.max.filesize设置为一个较大的数字并将拆分策略设置为ConstantSizeRegionSplitPolicy来禁用区域拆分。但是,您应该使用100GB之类的保护值,这样区域的增长不会超过区域服务器的能力。您可以考虑禁用自动拆分,并依赖于预分割的初始区域集,例如,如果您使用了用于密钥前缀的统一散列,并且<强>您可以确保每个区域的读/写负载及其大小在表>/P>区域中是一致的。 1) 请确保在将数据加载到hbase表2)Design good rowkey之前,可以使用杂音哈希或其他哈希技术对表进行预拆分,如下所述。确保各地区的分布均匀。
也看看

问题:有人能解释一下为什么会发生这种情况吗

原因非常明显且简单,由于该表的rowkey不好,将数据热点定位到一个特定的原因中

考虑java中的hashmap,其中包含hashcode为1234的元素。然后它会在一个桶里装满所有的元素,是吗?如果hashmap元素分布在不同的good
hashcode
中,那么它将把元素放在不同的存储桶中。hbase也是如此。在这里,您的hashcode就像您的rowkey…

此外,, 如果我已经有一个表,并且我想要分割区域,会发生什么 穿过

该类为选择手动拆分区域而不是让HBase自动处理区域的开发人员在管理生命周期中提供了一些实用程序

最有用的实用程序包括:
  • 创建具有指定数量预拆分区域的表
  • 对现有表上的所有区域执行滚动拆分
例如:

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);
其中-c 10将请求的区域数指定为10,-f指定表中所需的列族,以“:”分隔。该工具将创建一个名为“test_table”的表,其中包含10个区域:

$ hbase org.apache.hadoop.hbase.util.RegionSplitter test_table HexStringSplit -c 10 -f f1
如评论中所述,您发现我在写入hbase之前的最终RDD只有一个分区!这表明 只有一个执行者持有全部数据。。。我还在努力 找出原因。

还有,检查一下

spark.default.parallelism
默认为所有服务器上所有内核的数量 机器。并行化api没有父RDD来确定 分区数,因此它使用
spark.default.parallelism

因此您可以通过重新分区来增加分区。


注意:我注意到,在Mapreduce中,regions/input split的分区数=启动的映射程序数。。同样,在您的情况下,数据加载到一个特定区域的情况也可能相同,这就是一个执行器启动的原因。请验证一下,同样感谢您的帮助,我确实发现我的RDD通过一系列数据帧操作以某种方式进入了一个分区……尽管我不知道是哪个操作触发了分区塌陷为一个分区。我确实从hbase中读取了64个分区,通过查看sparkUI stages,我可以证明这一点。即使您的rdd仅位于spark的一个分区中,它也会根据密钥结构写入不同的hbase区域。我想您是在问spark为什么不以并行方式写入hbase(一个或多个区域,无关紧要)使用多个执行人。如果是,请告诉我。是的,你是对的。它是基于区域分割键写入多个区域的,因此只有一个执行者在写入,这会影响IO性能。感谢您的回答,我发现在写入hbase之前,我的最终RDD只有一个分区!这表明只有一个执行者持有整个数据。。。我还是很不舒服
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,}
...