在 Spark 性能调优中,经常会被建议尽量用 mappartition 操作去替代 map 操作。本文将会对这一论断背后的原因进行阐述。
一、map 操作 V.S. mappartition 操作
- map 操作:对 RDD 中的每个元素进行操作(可以理解为遍历),比如使用一个function则需要执行该 function n 次,其中 n 为元素个数;
- mappartition 操作:对 RDD 中每个 partition 的 iterator 进行操作,比如使用一个 function 则每个 partition 只需要各执行该 function 1 次(一个partition中的所有元素被一次传给该 function);
因此,存在如下结论:
- map 操作: 执行 1 次 function 只处理 1 个元素(或者称为一条数据),比如 partition 中的元素较多,当前已经处理了 1000 个元素,在内存不足的情况下,Spark 可以通过GC等方法(比如将已处理掉的 1000 个元素从内存中回收)回收内存。因此,通常 map 操作不会导致OOM的异常;
- mappartition 操作: 执行 1 次 function 需要接收该 partition 中的所有元素,因此一旦元素很多而处理内存不足,就容易导致OOM的异常;
- 一般而言,mappartition 的性能更高;初始化操作、数据库访问等操作适合使用 mappartition操作,这是因为:
- 假设需要对 RDD 中的每个元素做加密计算,在加密之前需要在每个 executor 中执行 initialization 操作,试想一下,如果该 initialization 放在 map 中执行将会导致该 initialization 被执行很多次,非常耗时;但是如果放在 mappartition 中则只需要每个 executor 中执行 1 次即可;
- 假设需要将 RDD 中的每个元素写入数据库中,这时候就应该把创建数据库的链接connection 操作放置在 mappartition 中,访问数据库操作本身就是个比较耗时的任务,如果该操作放在 map 中执行将会非常耗时且影响数据库的稳定。
二、foreach 操作 V.S. foreachpartition 操作
map 和 foreach 的区别在于:
- 前者是 transformation 操作(不会立即执行),后者是 action 操作(会立即执行);
- 前者返回值是一个新 RDD,后者没有返回值。
其他的和 map V.S. mappartition 类似。
笔者水平有限,如有错误,敬请指正!
网友评论