我在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.
答案 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与数据库合作。