- 分区函数
在Spark中, 对于map算子和foreach算子都提供了分区函数, 分别为mapPartitions() 和 foreachPartition()
map算子 和 foreach算子, 是针对RDD中每一个分区下每一个元素, 而分区函数则是针对每个分区的整个数据集
假设有如下数据集的RDD:
rdd = sc.parallelize([1,2,3,4,5,6,7,8,9,10],3)
查看这个RDD中各个分区的结果:
rdd.glom().collect()
结果:
[
[1, 2, 3],
[4, 5, 6],
[7, 8, 9, 10]
]
执行相关的操作: 将分区内每一个元素 + 1返回
def fn1(num):
return num + 1
rdd.map(fn1).glom().collect()
结果为:
[
[2, 3, 4],
[5, 6, 7],
[8, 9, 10, 11]
]
思考: fn1函数会被执行多少次呢? 10次, 在第一个分区内会被执行3次, 第二个分区会被执行3次, 第三个分区会被执行4次
假设: 如果在fn1函数中, 执行连接数据库的操作, 然后读取数据库中数据, 处理完成后, 将连接数据库的对象关闭, 请问, 这个打开连接和 关闭数据库连接操作会被触发多少次呢? 至少会被触发执行10次(有多少条数据, 就会被触发执行多少次),每一次创建连接和释放连接都要花费时间和资源, 频繁创建和关闭也会造成一定的资源浪费
思考: 打开连接和关闭连接, 由于业务要求, 必须放置到fn1中, 但是又想提升效率, 如何处理呢?
处理方案: 引入分区函数, 将map替换为mapPartitions来实现, 分区函数是针对每一个分区进行处理的
同样的案例:
def fn1(arr):
arr2 = []
for e in arr:
arr2.append(e + 1)
return arr2
rdd.mapPartitions(fn1).glom().collect()
结果为:
[
[2, 3, 4],
[5, 6, 7],
[8, 9, 10, 11]
]
此时 fn1函数只会被触发3次(rdd有多少个分区, 就会被触发多少次)
演示 foreach和foreachPartition函数
假设有如下数据集的RDD:
rdd = sc.parallelize([1,2,3,4,5,6,7,8,9,10],3)
查看这个RDD中各个分区的结果:
rdd.glom().collect()
结果:
[
[1, 2, 3],
[4, 5, 6],
[7, 8, 9, 10]
]
基于foreach 打印每一个数据
def fn1(num):
print(num)
rdd.foreach(fn1)
结果:
1
2
3
4
5
6
7
8
9
10
基于 foreachPartition打印:
def fn1(partitionData):
for e in partitionData:
print(e)
rdd.foreachPartition(fn1)
结果:
1
2
3
4
5
6
7
8
9
10
总结: 建议在使用map和foreach的时候, 建议更换mapPartitions和foreachPartition, 尤其是在函数中存在一些与资源相关的操作, 比如说 数据库的连接, IO操作。。。
分区函数: 作用在每一个分区上
非分区函数: 作用在每个分区的每一个元素上