如何使用Spark中的RowKey列表从Hbase进行并行读取

时间:2018-08-29 16:08:15

标签: apache-spark hbase

我有650万行键,想从 spark-job 中的hbase检索数据。如何从hbase并行检索结果?

我认为该代码段不会在执行程序上运行。

List<Get> listOFGets = new ArrayList<Get>();
Result[] results = Htable.get(listOFGets);

2 个答案:

答案 0 :(得分:0)

您可以通过创建RDD(以适合您的任何一种方式)然后调用.foreachPartition对象的方法JavaHBaseContext来对执行程序执行并行扫描。这样,HBaseContext会将Connection实例详细信息传递给函数类的每个实例,在该函数内部,您可以通过获取表等进行扫描。

由您决定如何创建RDD以适应此问题,以及应具有多少个元素(只需确保它具有与并行扫描一样多的分区)。以我的经验,您可以在spark上运行许多并发任务,可以执行许多并行扫描(当然,这取决于您的HBase集群强度)。

Java代码可能如下所示:

在主控上:

JavaHBaseContext hBaseContext = new JavaHBaseContext(sparkContext, HBaseConfig);
JavaRDD<blah> myRDD = ... (create an RDD with a number of elements)
hBaseContext.foreachPartition(myRDD,  new MyParFunction());

您的函数类将如下所示:

class MyParFunction implements VoidFunction<Tuple2<Iterator<blah>, Connection>>
{
@Override
    public void call(Tuple2<Iterator<blah>, Connection> t) throws Exception
    {
// Do your scan here, since you have the Connection object t
}
}

这应该在所有执行程序上并行运行扫描

答案 1 :(得分:0)

我通常使用.newAPIHadoopRDD()方法进行hbase扫描。请注意,这是Scala与Java API的相当丑陋的组合。您可以传入行键的任意列表(空白列表将返回表中的所有记录)。如果您的行键不是长编码的,那么您可能需要稍微修改一下代码。

def hbaseScan(ids: List[Long]): Dataset[Result] = {
  val ranges = ListBuffer[MultiRowRangeFilter.RowRange]()
  //converts each id (Long) into a one element RowRange
  //(id gets implicitly get converted to byte[])
  ids.foreach(i => {
    ranges += new MultiRowRangeFilter.RowRange(i, true, i + 1, false)
  })

  val scan = new Scan()
  scan.setCaching(1000) /* fetch 1000 records in each trip to hbase */
  scan.setCacheBlocks(false) /* don't waste hbase cache space, since we are scanning whole table

  if (ranges.nonEmpty) {
    //The list of RowRanges is sorted and merged into a single scan filter
    scan.setFilter(new MultiRowRangeFilter(MultiRowRangeFilter.sortAndMerge(ranges.asJava)))
  }

  val conf = HBaseConfiguration.create()
  conf.set(TableInputFormat.INPUT_TABLE, HBASE_TABLE /*set your table name here*/)
  conf.set(TableInputFormat.SCAN, scan)

  spark.sparkContext.newAPIHadoopRDD(
    conf,
    classOf[TableInputFormat],
    classOf[ImmutableBytesWritable],
    classOf[Result]
  ).toDF("result").as[Result]
}

这将返回一个Dataset[Result],其分区数与扫描表中的区域相同。抱歉,我没有任何等效的Java代码可以共享。

编辑:处理不正确的注释

我应该以为这个方法在读取整个hbase表或少量任意行键时效果最好。我的用例正好同时完成了这两个操作,因为我总是一次查询1000个行键或整个表,而中间却什么也没有。

如果任意行键的数量很大,则MultiRowRangeFilter.sortAndMerge()步骤将挂断一个核心。可以扩展此方法,以在创建用于扫描的Filter之前,将键列表排序和合并到键范围中的过程并行化。排序和合并后,此方法的确确实可以在您拥有区域的多个分区上并行运行,并且如果您有许多连续的行键范围,甚至可以减少往返hbase的次数。

很难说这个过程是否比在整个集群上散布随机数据更为有效,因为它完全取决于许多因素:记录大小,表大小,行键范围等。我相信许多用例中,这种方法会更有效,但显然不是每个用例都可以。