Apache Spark映射并通过传递值减少

时间:2014-11-23 18:12:15

标签: scala cassandra apache-spark

我在从Cassandra加载的RDD上有一个简单的mapreduce工作。 代码看起来像这样

sc.cassandraTable("app","channels").select("id").toArray.foreach((o) => {

  val orders = sc.cassandraTable("fam", "table")
    .select("date", "f2", "f3", "f4")
    .where("id = ?", o("id")) # This o("id") is the ID i want later append to the finished list

  val month = orders
    .map( oo => {
      var total_revenue = List(oo.getIntOption("f2"), oo.getIntOption("f3"), oo.getIntOption("f4")).flatten.reduce(_ + _)
      (getDateAs("hour", oo.getDate("date")), total_revenue)
    })
    .reduceByKey(_ + _)
})

所以这段代码总结收入并返回类似这样的内容

(2014-11-23 18:00:00, 12412)
(2014-11-23 19:00:00, 12511)

现在我想把它保存回Cassandra Table revenue_hour,但我需要在该列表中以某种方式使用ID,就像那样。

(2014-11-23 18:00:00, 12412, "CH1")
(2014-11-23 19:00:00, 12511, "CH1")

如何使用更多(键,值)列表来完成此工作?我如何传递更多值,这些值不应该被转换,而只是传递到最后,以便我可以将它保存回Cassandra?

2 个答案:

答案 0 :(得分:3)

也许你可以使用一个类并通过流程来处理它。我的意思是,定义RevenueHour类

case class RevenueHour(date: java.util.Date,revenue: Long, id: String)

然后在地图阶段构建一个中间RevenueHour,然后在reduce阶段构建另一个。

val map: RDD[(Date, RevenueHour)] = orders.map(row => 
  (
    getDateAs("hour", oo.getDate("date")), 
    RevenueHour(
      row.getDate("date"),
      List(row.getIntOption("f2"),row.getIntOption("f3"),row.getIntOption("f4")).flatten.reduce(_ + _),
      row.getString("id")
    )
  )
).reduceByKey((o1: RevenueHour, o2: RevenueHour) => RevenueHour(getDateAs("hour", o1.date), o1.revenue + o2.revenue, o1.id))

我使用o1 RevenueHour,因为o1和o2都有相同的密钥和相同的id(因为之前的where子句)。

希望它有所帮助。

答案 1 :(得分:1)

该问题的方法是通过迭代一组id并仅对一个(可能很小的)数据子集应用Spark作业来对数据处理进行排序。

不知道'频道之间的关系如何?和'表'数据,我看到两个选项可以充分利用Spark并行处理数据的能力:

选项1

如果'表格中的数据为' table(称为" orders"从此处开始)包含报告中我们需要的所有id组,我们可以将报告逻辑应用于整个表:

基于这个问题,我们将使用这个C *架构:

CREATE TABLE example.orders (id text,
      date TIMESTAMP,
      f2 decimal,
      f3 decimal,
      f4 decimal,
      PRIMARY KEY(id, date)
);

通过提供表示表格架构的案例类,可以更轻松地访问cassandra数据:

case class Order(id: String, date:Long, f2:Option[BigDecimal], f3:Option[BigDecimal], f4:Option[BigDecimal]) {
    lazy val total = List(f2,f3,f4).flatten.sum
}

然后我们可以根据cassandra表定义一个rdd。当我们提供case类作为类型时,spark-cassandra驱动程序可以直接执行转换以方便我们:

val ordersRDD = sc.cassandraTable[Order]("example", "orders").select("id", "date", "f2", "f3", "f4")

val revenueByIDPerHour = ordersRDD.map{order => ((order.id, getDateAs("hour", order.date)), order.total)}.reduceByKey(_ + _) 

最后回到卡桑德拉:

revenueByIDPerHour.map{ case ((id,date), revenue) => (id, date, revenue)}
    .saveToCassandra("example","revenue", SomeColumns("id", "date", "total"))

选项2

如果(" app"," channels")表中包含的id应该用于过滤id组(例如有效ID),那么,我们可以加入带有订单的此表中的ID。这项工作将类似于之前的工作,增加了:

val idRDD = sc.cassandraTable("app","channels").select("id").map(_.getString)
val ordersRDD = sc.cassandraTable[Order]("example", "orders").select("id", "date", "f2", "f3", "f4")
val validOrders = idRDD.join(ordersRDD.map(order => (id,order))

这两种方式说明了如何使用Cassandra和Spark,利用Spark操作的分布式特性。它还应该比对“频道”中的每个ID执行查询要快得多。表