Spark2.0.X算子源码深度剖析之MapPartitionsRDD,绝对让你看清楚算子的计算本质

来源:互联网 发布:手机荧光字软件 编辑:程序博客网 时间:2024/06/08 03:50

微信号:519292115

邮箱:taosiyuan163@163.com


尊重原创,禁止转载!!


最近因为公司的事情 一直没来得及写博客..

目前公司项目需要用到SparkStreamingOnKudu,,今晚撸代码的时候看了看Streaming的RDD算子,略有感悟,索性又来写写博客,记录下心得

网上好多帖子讲的并不很清晰,大部分是走读模式,也只是知其然不知其所以然.

这次本人主要会剖析map和mapPartitions,若你能理解出十之八九 那么看其他RDD算子源码也是易如反掌了


从Streaming的map函数开始(底层调用的就是RDD的map函数)


SparkRDD大部分算子都是以传名参数的形式使用的,而底层计算逻辑实现大多也是如此,就如同像Java的类部类,你只需要实现里面的接口,

而传名参数不但可以通过方法传入 还能 作为类的参数, 后面有提及..


上面的map只是new了一个DStream的map函数中的MappedDStream,参数就是当前我们从kafka读取到的InputDStream(继承于DStream)




和参数mapFunc它本身,这里只是做了闭包的序列化检查,然后原封不动的返回


MappedDStream继承于DStream的其中一个算子,就如同继承于RDD的其他算子的概念

这里的compute就是它的真正函数计算入口,里面的getOrCompute就会把DStream转换成RDD,并会做优化计算,包括调用之前持久化的RDD或者重算,和RDDStage划分中的计算逻辑大同小异(可以参考我之前博客写过的RDD的Stage划分算法有介绍过).在拿到当前时间段的算子后调用2个map




第一个map是Option的map(Spark里有好多map,后面还有Iterator的map,所有实现都不一样!)

因为getOrCompute返回的是Option的RDD,可能会为空


拿到的当前RDD这次就会调用RDD的map函数了.而传入的参数正是之前我们map函数中的传名参数


进来入RDD,核心点来了:这里的sc.clean(f)返回的就是f本身,会被被声明为cleanF并最后会被圆圈中的iter.map调用

其实这里就MapPartitionsRDD类中的传名参数的实现返回值调用了map函数并且以我们最开始的传名参数(f)传入进去


上面的map函数里面new的就是下图中集成于RDD的MapPartitionsRDD.

而当这个算子被触发的时候会调用compute(细节之前在Stage和Task划分章节有提及到)函数中的传名参数f实现,而后面的Iterator是重点,就是上图中的iter.map(cleanF)他会把firstParent[T].iterator(split, context)的返回结果做map(cleanF),而cleanF正是我们最开始map传进来的传名参数.


最终计算入口就是它了:


里面其实就是调用firstParent[T].iterator(split, context)返回的iterator(里面装的就是每个partition为单位的迭代器)的map函数,然后将每个partition做成iterator(里面装的就是每个元素为单位)


所以我们在用map算子操作的时候 其实就是计算出当前第一个父RDD 把它转换成每个分区中的最小单位组成的迭代器.

mapPartitions和map一样,底层使用的还是MapPartitionsRDD,而他们最大的区别就是少了上图的哪个步骤,只是把RDD转换成了以partition组成的iterator如图:




最后我为了验证上述的论证做了个简单的实验;


确实如此,迭代出来的mapPartitions的每个元素就是每个分区 .分区里面又能迭代出来每个分区的每个原始数据单元



~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

原创粉丝点击