forked from spirom/LearningSpark
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathHashJoin.scala
More file actions
68 lines (55 loc) · 2.34 KB
/
Copy pathHashJoin.scala
File metadata and controls
68 lines (55 loc) · 2.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package special
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkContext, SparkConf}
import scala.collection.mutable
// This gives is access to the PairRDDFunctions
import org.apache.spark.SparkContext._
// encapsulate a small sequence of pairs to be joined with pair RDDs --
// making this serializable effectively allows the hash table to be
// broadcast to each worker
// Reference: http://en.wikipedia.org/wiki/Hash_join
// (this is specifically an inner equi-join on pairs)
class HashJoiner[K,V](small: Seq[(K,V)]) extends java.io.Serializable {
// stash it as a hash table, remembering that the keys may not be unique,
// so we need to collect values for each key in a list
val m = new mutable.HashMap[K, mutable.ListBuffer[V]]()
small.foreach {
case (k, v) => if (m.contains(k)) m(k) += v else m(k) = mutable.ListBuffer(v)
}
// when joining the RDD, remember that each key in it may or may not have
// a matching key in the array, and we need a result tuple for each value
// in the list contained in the corresponding hash table entry
def joinOnLeft[U](large: RDD[(K,U)]) : RDD[(K, (U,V))] = {
large.flatMap {
case (k, u) =>
m.get(k).flatMap(ll => Some(ll.map(v => (k, (u, v))))).getOrElse(mutable.ListBuffer())
}
}
}
object HashJoin {
def main (args: Array[String]) {
val conf = new SparkConf().setAppName("HashJoin").setMaster("local[4]")
val sc = new SparkContext(conf)
val smallRDD = sc.parallelize(
Seq((1, 'a'), (1, 'c'), (2, 'a'), (3,'x'), (3,'y'), (4,'a')),
4)
val largeRDD = sc.parallelize(
for (x <- 1 to 10000) yield (x % 4, x),
4
)
// simply joining the two RDDs will be slow as it requires
// lots of communication
val joined = largeRDD.join(smallRDD)
joined.collect().foreach(println)
// If the smaller RDD is small enough we're better of with it not
// being an RDD -- and we can implement a hash join by hand, effectively
// broadcasting the hash table to each worker
println("hash join result")
// NOTE: it may be tempting to use "collectAsMap" below instead of "collect",
// and simplify the joiner accordingly, but that only works if the keys
// are unique
val joiner = new HashJoiner(smallRDD.collect())
val hashJoined = joiner.joinOnLeft(largeRDD)
hashJoined.collect().foreach(println)
}
}