怎样浅谈Spark的多语言支持,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
Spark 设计上的优秀无容置疑,甫一出道便抢了 Hadoop 的 C 位,在开源大数据的黄金十年里一时风头无两,在人工智能时代的当下仍然能够与时俱进,不可谓不牛逼。架构和设计上的卓越,不遑多言,美中不足之处自然也有不少,比如调度模型跟 MapReduce 这种计算范式过于耦合,Spark 最近引入 Barrier 调度模式就是为了支持深度学习这种新的计算类型,所幸在于对框架的改动不会伤经动骨。有些缺陷则不然,影响全局,调整起来绝非易事。这儿我主要想谈下 Spark 的框架语言实现和多语言支持,谈它的得与失,不过主要还是谈问题谈缺陷。
Spark 核心和框架构建在 Scala 上,对应用开发者也是言必称 Scala,上来就秀代码极简的漂亮肌肉。在用户接口上,从企业级应用的首选 Java,到数据科学家的 Python 和 R,再到商业智能 BI 的 SQL,官方都一一支持,按说已经很全面很完整了,各方神圣也都伺候到了,还有什么抱怨的,言何缺陷?我们来看下目前存在的问题,希望不会变成鸡蛋里挑骨头。
首先,对 Python 的支持也就是 PySpark 复杂且效率低下,有点像 hack,十分像早期 Spark 攻城略地的产出。其实对 R 的支持 SparkR 也如出一辙,我们讨论清楚了 PySpark 也就明白了对其他语言的支持其实是类似的。从使用者的角度来看用 PySpark 写程序的体验还是很不错的,跟 Scala 有点类似,简洁而优雅,就跟写个单机程序一样,不需要考虑分布式处理的复杂性。然而在这件漂亮的外衣下,用 PySpark 写的 Spark 程序就是比 Scala 版本慢,还动不动 OOM 掉。
为什么?我们来探一下究竟。一般 Spark 程序首先给定输入也就是要处理的数据集,然后表达好针对这个数据集的每行记录要处理的逻辑和转换规则,再将结果打印出来或者保存到文件。背后 Spark 框架进行翻译转换,产出一个个 RDD 和这些 RDD 之间的关系,每个 RDD 表示对应的数据集和在该数据集上要执行的变换处理逻辑。这些 RDD 根据它们之间的依赖关系组成 DAG,对 DAG 翻译转换成 stages 和 tasks,这个过程在 driver 端由框架 Scala 代码完成,包括对 stages 和 tasks 的调度执行;RDD 的处理逻辑对应用户的 Scala 或 Python 代码,因为要分布式并发处理,主要在 executor 上执行。因此,对于 Python 版本的 Spark 程序,在 driver 上和 executor 上都有要执行的 Python 代码,必然需要对应的 Python 解释器来执行;然而 Spark 计算框架是 Scala/Java 代码实现的,driver 和 executor 整体上得跑在 JVM 进程里面。那么如何在 driver 端和 executor 上同时执行代表用户逻辑的 Python 代码和核心引擎的 JVM 代码,并在两者之间进行交互和协调呢?PySpark 的做法是在 driver 端和 executor 上伴随必需的 JVM 进程,再 launch 起来单独的 Python 解释执行进程,然后通过 socket,文件和 pipeline 进行交互和协作。这不能不说是个非常低效的做法,因为 Spark 程序通常而言并非一个普通的应用,要处理非常大的数据集。对于成千上万行记录的处理,都要在 executor 上通过跨进程管道到 Python 进程上来回一趟,末了在 driver 上为了传递计算结果可能还要写个磁盘文件才能转给 Python 进程,为此涉及到大量记录数据在 Python 和 Java 之间要序列化和反序列化,效率可想而知。开启新的 Python 进程直接执行用户的代码省事倒是省事,除了效率问题,考虑到进程管理,内存控制,容器环境和数据安全,这样做的代价还是很大的,然而 Spark 却不得不这样做,主要是因为受限于它在语言支持上面缺乏长远的考虑和整体的设计,这就是我想讲的最重要的问题。
在语言支持上,从一开始 Spark 的关注点就是“糖衣”而非“炮弹”,在用户体验上追求数据处理逻辑表达的简洁有力,因此完胜当时的各种计算框架而后一骑绝尘。记得在 Hadoop 如日中天的时候,笔者所在的 Intel 大数据团队正在玩 Hadoop 不亦乐乎,Spark 的作者 Matei 先生在远程会议上向我们演示当时还十分稚嫩的 Spark,看到那个有名的 MapReduce Helloworld 例子 WordCount 程序只需要两三行 Scala 代码搞定,真是骇人。Spark 能够在短时间内抢了 Hadoop 的风头,除了性能更快,毫无疑问这也是个关键因素。然而这种简洁有力,在多大程度上要归功于 Scala,其实不太好讲。感觉最本质的还是在于函数式语言的支持, RDD 本身的抽象,丰富的算子支持和巧妙的关系推导,这部分可以归为引擎层面,大体上用其他语言,比如更擅长做系统框架的,C++,应该也能实现得出来。至于用户的程序是用 Scala 还是 Python,只要支持 closures,应该都能行,简洁程度上不会差别太大,也完全可以多支持一些语言,每个用户具体用哪一个要看个人的偏好,没有道理让大家都去学一个新的语言。可惜的是,Spark 引擎的实现应该是受到了主要用户接口语言 Scala 的影响,也采用了 Scala,部分采用 Java,但本质上都是 JVM 语言,这样做的好处是一锅煮,省事。考古学家可以列出一大堆使用 Scala 的好处,比如代码行数少,codegen 给力之类的,但是这些都是局部的,从整体上 Spark 作为一个统一的大数据处理平台,其实更需要长远的通盘考虑。这个考虑就是在用户接口上要能够更快更好地支持更多的用户语言,在计算引擎上能够支持硬件层面的极致性能优化,在计算场景上也能支持其他计算引擎的集成。
这个考虑简单来说就是缺乏对框架层面的 C/C++ 支持。当然这个支持很难在第一天就考虑进来,毕竟关注点不在这儿,那个时候估计 Matei 先生也想不到 Spark 会这么成功。然而框架层面缺乏对 C/C++ 的原生支持或者换句话说,框架核心没有采用 C/C++ 这种 native 语言来开发,其弊端和对 Spark 今后长远发展的影响毫无疑问显而易见。C/C++ 作为系统语言,下可以跟硬件直接打交道,上可以直接对接各种开发语言,因此也不难支持各种计算处理库和引擎。Spark 本身定位为一个大数据处理平台,如果核心公共逻辑和操作交给 C/C++ 来实现,那么首先支持 Python 这种用户接口语言,直接利用 FFI 机制调用就是了,简单且无损效率。主流的开发语言基本上都很好地支持 C/C++,Python 不说,像 Java,Go,Rust,Julia,都是。Spark 添加一个新的语言支持,就简化为添加一个新的 binding,还好维护,也不用担心这个功能被阉割,那个支持还不全。对新的硬件做优化也不成问题,比如从计算新贵 GPU 到 RDMA,再到可持久化大内存 AEP,根本不会受限于 JVM 的限制,各大硬件厂商都能自己撸起袖子开搞,Spark 坐享其成就是。现在对这些新硬件的支持为什么上不了,社区不接受?个人认为主要还是缺乏框架层面的支持,搞出来的都像是 hack,进去也是麻烦。看看人家 Tensorflow 就知道了,CPU,TPU,GPU 都能玩得转。核心框架采用 Scala/Java 来实现,还有一个重要的影响是数据集的内存表示,早期 Spark 直接用 Java 对象来表示数据记录,需要持久化或者网络传输要序列化和反序列化,当然在这一点上 Spark 后来很快意识到了问题,通过 offheap 和 Tunsten 项目马上把问题解了,所幸还有招数。如果是 C/C++ 实现的,数据的内存布局必然会采取类似 Arrow 的这种中性表示,方便各种用户接口语言来访问和操作,就像深度学习框架里面常见的 tensor,必须考虑高效传递。最后一点,采用 Scala/Java 来实现的 Spark 框架,虽然整合开源大数据领域和企业级应用里面很常见的各种数据源得心应手,但要整合集成更多的计算框架则就力不从心了。类似于对新硬件的支持,看一下 Yahoo 对 Caffe 和 Tensorflow 的支持就明白了,只能 hack 一下自己玩,不可能融入到 Spark 里面流行起来,要原生支持 deep learning,像 machine learning,可以,但要自己撸。Spark 自己花了很大力气搞 mllib,Intel 搞 BigDL,都是没办法,因为缺乏核心框架的给力支持,不能直接把这些领域里面现成的库实现和计算引擎集成进来。如果核心框架是是 C/C++ 实现的,集成 PyTorch 和 Tensorflow 能有多大的问题?无非是多挂一个动态模块或搞一个扩展插件的事情。
关于怎样浅谈Spark的多语言支持问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。