如何从spark中的hbase表中获取所有数据

时间:2014-07-02 14:56:46

标签: java mapreduce hbase bigdata apache-spark

我在hbase中有一个名为UserAction的大表,它有三个列族(歌曲,专辑,歌手)。我需要从'song'列族中获取所有数据作为JavaRDD对象。我试试这段代码,但效率不高。有没有更好的解决方案呢?

    static SparkConf sparkConf = new SparkConf().setAppName("test").setMaster(
        "local[4]");
static JavaSparkContext jsc = new JavaSparkContext(sparkConf);

static void getRatings() {

    Configuration conf = HBaseConfiguration.create();
    conf.set(TableInputFormat.INPUT_TABLE, "UserAction");
    conf.set(TableInputFormat.SCAN_COLUMN_FAMILY, "song");

    JavaPairRDD<ImmutableBytesWritable, Result> hBaseRDD = jsc
            .newAPIHadoopRDD(
                    conf,
                    TableInputFormat.class,
                    org.apache.hadoop.hbase.io.ImmutableBytesWritable.class,
                    org.apache.hadoop.hbase.client.Result.class);

    JavaRDD<Rating> count = hBaseRDD
            .map(new Function<Tuple2<ImmutableBytesWritable, Result>, JavaRDD<Rating>>() {

                @Override
                public JavaRDD<Rating> call(
                        Tuple2<ImmutableBytesWritable, Result> t)
                        throws Exception {
                    Result r = t._2;
                    int user = Integer.parseInt(Bytes.toString(r.getRow()));
                    ArrayList<Rating> ra = new ArrayList<>();

                    for (Cell c : r.rawCells()) {

                        int product = Integer.parseInt(Bytes
                                .toString(CellUtil.cloneQualifier(c)));
                        double rating = Double.parseDouble(Bytes
                                .toString(CellUtil.cloneValue(c)));

                        ra.add(new Rating(user, product, rating));
                    }

                    return jsc.parallelize(ra);
                }
            })
            .reduce(new Function2<JavaRDD<Rating>, JavaRDD<Rating>, JavaRDD<Rating>>() {
                @Override
                public JavaRDD<Rating> call(JavaRDD<Rating> r1,
                        JavaRDD<Rating> r2) throws Exception {
                    return r1.union(r2);
                }
            });
    jsc.stop();
}

宋列家庭方案设计是:

RowKey = userID, columnQualifier = songID and value = rating.

1 个答案:

答案 0 :(得分:1)

更新:好的,我现在看到你的问题了,因为一些疯狂的原因你将数组转换为RDD return jsc.parallelize(ra);。你为什么这样做?你为什么要创建RDD的RDD?为什么不将它们作为数组?执行reduce操作后,可以连接数组。 RDD是一种抗性分布式数据集 - 拥有分布式数据集的分布式数据集在逻辑上没有意义。我甚至惊讶你的工作,并没有崩溃!无论如何,这就是为什么你的工作太慢了。

无论如何,在你的地图之后的Scala中,你只需要flatMap(identity)并将所有列表连接在一起。

我真的不明白为什么你需要做一个减少,也许这就是你有一些效率低下的地方。这是我读取HBase表的代码(它的通用 - 即适用于任何方案)。有一点需要确保在您阅读HBase表时确保分区数量合适(通常您需要很多)。

type HBaseRow = java.util.NavigableMap[Array[Byte],
  java.util.NavigableMap[Array[Byte], java.util.NavigableMap[java.lang.Long, Array[Byte]]]]
// Map(CF -> Map(column qualifier -> Map(timestamp -> value)))
type CFTimeseriesRow = Map[Array[Byte], Map[Array[Byte], Map[Long, Array[Byte]]]]

def navMapToMap(navMap: HBaseRow): CFTimeseriesRow =
  navMap.asScala.toMap.map(cf =>
    (cf._1, cf._2.asScala.toMap.map(col =>
      (col._1, col._2.asScala.toMap.map(elem => (elem._1.toLong, elem._2))))))

def readTableAll(table: String): RDD[(Array[Byte], CFTimeseriesRow)] = {
  val conf = HBaseConfiguration.create()
  conf.set(TableInputFormat.INPUT_TABLE, table)
  sc.newAPIHadoopRDD(conf, classOf[TableInputFormat],
    classOf[org.apache.hadoop.hbase.io.ImmutableBytesWritable],
    classOf[org.apache.hadoop.hbase.client.Result])
  .map(kv => (kv._1.get(), navMapToMap(kv._2.getMap)))
}

如您所见,我不需要减少代码。这些方法非常自我解释。我可以深入研究你的代码,但我缺乏阅读Java的耐心,因为它非常冗长。

我有一些专门用于从行中获取最新元素的代码(而不是整个历史记录)。如果你想看到它,请告诉我。

最后,建议您考虑使用Cassandra而不是HBase,因为datastax与数据库合作。