Spark只写一个hbase区域服务器

时间:2017-02-03 18:23:00

标签: apache-spark hbase rdd

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?

2 个答案:

答案 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

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

注意:我观察到,在Mapreduce中区域/输入分区的分区数=启动的映射器数量。类似地,在您的情况下,可能是相同的情况,其中数据加载到一个特定区域,这就是为什么一个执行器lauched。请同样验证

答案 1 :(得分:1)

虽然您没有提供示例数据或足够的解释,但这主要不是由于您的代码或配置。 由于非最佳的rowkey设计,它正在发生。 您正在编写的数据是键(hbase rowkey)结构不正确(可能单调增加或其他)。因此,写入其中一个区域正在发生。您可以防止这种情况发生。各种方式(各种推荐的rowkey设计实践,如盐渍,反转和其他技术)。 作为参考,您可以看到http://hbase.apache.org/book.html#rowkey.design

如果您想知道写入是针对所有区域并行进行还是逐个进行(不清楚问题)请查看: http://hbase.apache.org/book.html#_bulk_load