在Spark的groupByKey和countByKey中使用JodaTime

时间:2015-01-27 12:09:42

标签: jodatime apache-spark

我有一个非常简单的Spark程序(在Clojure中使用Flambo,但应该很容易理解)。这些是JVM上的所有对象。我正在local实例上进行测试(虽然我猜测Spark仍会序列化和反序列化)。

(let [dt (t/date-time 2014)
      input (f/parallelize sc [{:the-date dt :x "A"}
                               {:the-date dt :x "B"}
                               {:the-date dt :x "C"}
                               {:the-date dt :x "D"}])
      by-date (f/map input (f/fn [{the-date :the-date x :x}] [the-date x])))

输入是四个元组的RDD,每个元组具有相同的日期对象。第一个映射产生date =>的键值RDD。 X。

input的内容符合预期:

=> (f/foreach input prn)
[#<DateTime 2014-01-01T00:00:00.000Z> "A"]
[#<DateTime 2014-01-01T00:00:00.000Z> "B"]
[#<DateTime 2014-01-01T00:00:00.000Z> "C"]
[#<DateTime 2014-01-01T00:00:00.000Z> "D"]

为了清楚起见,平等和.hashCode处理日期对象:

=> (= dt dt)
true
=> (.hashCode dt)
1260848926
=> (.hashCode dt)
1260848926

他们是JodaTime DateTime的实例,implement equals as expected

当我尝试countByKey时,我得到了预期的结果:

=> (f/count-by-key by-date)
{#<DateTime 2014-01-01T00:00:00.000Z> 4}

但是当我groupByKey时,它似乎无法奏效。

=> (f/foreach (f/group-by-key by-date) prn)
[#<DateTime 2014-01-01T00:00:00.000Z> ["A"]]
[#<DateTime 2014-01-01T00:00:00.000Z> ["B"]]
[#<DateTime 2014-01-01T00:00:00.000Z> ["C"]]
[#<DateTime 2014-01-01T00:00:00.000Z> ["D"]]

密钥都是相同的,所以我希望结果是一个条目,以日期为关键字,["A", "B", "C", "D"]为值。发生了一些事情,因为这些值都是列表。

不知怎的groupByKey没有正确地将键等同起来。但是countByKey是。这两者之间的区别是什么?我怎样才能使它们的行为相同?

有什么想法吗?

1 个答案:

答案 0 :(得分:3)

我越来越接近答案了。我认为这属于答案部分,而不是问题部分。

按键分组,变成本地收集,提取第一项(日期)。

=> (def result-dates (map first (f/collect (f/group-by-key by-date))))
=> result-dates
(#<DateTime 2014-01-01T00:00:00.000Z>
 #<DateTime 2014-01-01T00:00:00.000Z>
 #<DateTime 2014-01-01T00:00:00.000Z>
 #<DateTime 2014-01-01T00:00:00.000Z>)

哈希码都是一样的

=> (map #(.hashCode %) result-dates)
(1260848926
 1260848926
 1260848926 
 1260848926)

毫秒是完全相同的:

=> (map #(.getMillis %) result-dates)
(1388534400000
 1388534400000
 1388534400000
 1388534400000)

equals失败,但isEquals成功

=> (.isEqual (first result-dates) (second result-dates))
true

=> (.equals (first result-dates) (second result-dates))
false

documentation for .equals says

  

根据毫秒时刻和年表

,将此对象与指定对象进行相等性比较

他们的毫秒数是相等的,他们的年代表似乎是:

=> (map #(.getChronology %) result-dates)
(#<ISOChronology ISOChronology[UTC]>
 #<ISOChronology ISOChronology[UTC]>
 #<ISOChronology ISOChronology[UTC]>
 #<ISOChronology ISOChronology[UTC]>)

但是,年代表不等

=> (def a (first result-dates))
=> (def b (second result-dates))

=> (= (.getChronology a) (.getChronology b))
false

虽然哈希码可以

=> (= (.hashCode (.getChronology a)) (.hashCode (.getChronology b)))
true

但是joda.time.Chronology没有提供its own equals method并从Object继承它,它只使用引用相等性。

我的理论是,这些日期都是用自己独立的,不同的,构建的年代学对象来反序列化的,但是JodaTime有its own serializer可能会解决这个问题。也许自定义Kryo序列化程序会在这方面提供帮助。

目前,我在Spark中使用JodaTime的解决方案是通过调用toInstantjava.util.Date而非{{3}来使用org.joda.time .Instant }。

两者都涉及丢弃时区信息,这不是理想的,所以如果有人有更多的信息,那将是非常受欢迎的!