Spark学习笔记(2)

  • 最常见单词计数问题
val input = sc.textFile(file_path)
val wordsRDD = input.map(x => x.split(" "))
# method 1
val result = wordsRDD.map(x => (x, 1)).reduceByKey((x, y) => x + y)
# method 2
val result = wordsRDD.countByValue()
  • combineByKey的工作原理
    当第一次在分区内遇到某个键,使用createCombiner()函数创建该键对应的累加器的初始值;当之后再在该区间内遇到此键,使用mergeValue()方法将累加器对应的当前值与新的值进行合并。

  • 对每个键规约数据的等价做法

rdd.groupByKey().map(value => value.reduce(func)) # 需要为每个键创建存放值的列表,占用内存

等价于

rdd.reduceByKey(func) # 比前者更高效
  • 连接操作
    左连接(leftOuterJoin),右连接(rightOuterJoin); 前者保留源RDD的所有键去匹配第二个RDD中与之相同的键,而后者则保留第二个RDD的所有键去匹配源RDD中与之相同的键。若同一个RDD中有多个相同键,则匹配后一一列出所有符合条件匹配对(不会压缩到一条记录中,注意)。倘若找不到相同的键,则在对应处记录None。

  • 数据分区
    必要性:对数据集在节点间的分布进行控制,以最少的网络传输极大提升整体性能;
    非充分性:若RDD只需一次扫描,则无需分区;需要分区的场景包括大量基于键的连接计算。

例:
对于 rddA.join(rddB)操作,该操作会先将rddA和rddB中的值全部求出来,将哈希值相同的记录通过网络传到同一台机器上,再在各个机器上对键相同的元素进行连接操作,即历经「rddA的hash值计算+跨数据混洗+相同键返回」与「rddB的hash值计算+跨数据混洗+相同键返回」两大步骤,再比较低效。
但若先对rddA执行自定义分区,即

rddA = rddA.partitionByKey(new HashPartitioner(N)).persist()

其中N为分区个数,persist为持久化操作(必须有,否则后续每次用到rddA时都会重复对数据做分区操作,相当于把partitionByKey的作用抵消了,和之前未自定义分区效果一样)。从而join操作时不会对rddA再进行混洗,只对rddA做本地引用,对rddB做混洗,网络传输仅存在于混洗rddB上。

猜你喜欢

转载自blog.csdn.net/weixin_42365868/article/details/109700958