这是一个很好玩的问题。从某种程度而言,Scala是不善于处理大数据的。作为一个函数式语言,必须在内存消耗和性能消耗两者之间徘徊,而普通的命令式语言就并不会有这种问题。举个例子,从数据结构来看,函数式语言要求不能修改原有结构(如果修改了,就不再吻合Immutable这一黄金定律),对于普通的链表(链表List在函数式语言中比数组Array更常见),每当你做一次操作,比如增加元素,删减元素等等,照理说会生成一个新的链表,而非像过程式语言,直接通过指针对链表本身进行修改。为了让操作速度达到与过程式语言类似或者相匹配,函数式语言的天才们发明了很多种不同方法,比如用结构分享(Structural Sharing)的技巧来应付链表,每次操作只记录下那一项特殊操作,而不毁坏或者替代原有链表。对更高级一些的结构,比如哈希图(HashMap),普通命令式语言用哈希列表(HashTable)这种简单的方式来执行,但悲壮的函数式语言就必须依赖于2-3拇指树(2-3 Finger Trie)一类的高端结构来达到相同的操作效率。但虽然速度达到了,占用空间就成了一个问题。当命令式语言通过不停对同一个对象进行修改的同时,函数式语言却不停的生成新内容。所以虽然函数式语言在理论上(无限空间与无限时间),数学上,都是更高档次的语言,但在残酷的现实面前,却有时赶不上命令式语言。离散数学(Discrete Mathematics)中对时间的定义,只把Polynomial(多项式时间)和Exponential(指数级时间)分开,认为多项式时间在宇宙尺度上就已经足够快了,但在争分夺秒的实际程序中,多项式还完全不够,线性(Linear)时间都嫌长。
为了让函数式语言达到函数式语言编程者们心目中“神”一样的地位,无数的东西被发明了出来,全世界第一个编译器就是为Lisp这个函数式语言发明的,这个编译器还自带全世界第一个垃圾处理器(Garbage Collection),第一个实现递归函数(1960年麦卡锡在ACM上发表了论文:《递回函数的符号表达式以及由机器运算的方式,第一部》),发明了树结构,动态类型(Dynamic Typing)——如今JavaScript, Python, Ruby, PHP等都非常依赖的动态类型,甚至还有条件语句。准确而言,现代编程几乎都是由函数式语言奠定的基础,所有命令式语言的高级功能,比如Java的反射,泛型,Java8的Lambda方程,Ruby的宏,全部都是函数式语言的馈赠。所以你想,当在你心目中,这个世界上最牛逼的东西居然内存和效率都拼不过比它挫的语言模式时,函数式的程序员能不恼怒吗?这恼怒带来了两点:1. 死不认账。函数式程序员绝对不会承认它们的语言效率或内存占用 比命令式语言糟糕;2. 拼命改进,最终又为计算机科学做出了无数贡献。
所以说,如果Scala在内存和效率上似乎都不占太多优势(当然,越熟悉Scala,函数式编程能力越强的人,这两点都将不是问题,通过聪明的设计算法,选用正确的数据结构,速度和高效内存利用肯定和命令式语言的程序近似),为什么人们会选择Scala作为大数据的语言?这里涉及到几个原因:
1. Scala 具有很完整又很强大的集合处理能力,准确而言是现代语言中最有优势的一个。Scala拥有庞大而完整的集合类库,比如Set, List, Vector, Tuple, Map,而有效的泛型能让你肆意组合这些类型得到新的类型,比如像这样的类别:List[Map[String, (Int, List[String)]]],这是一个链表,每个链表元素是一个映射,映射中用字符做key,另一个Tuple元组做value(值),这个元组的第一个元素是整数,第二个元素是一个链表,这样的集合在其他语言中不是不可以做到,但很难,想想在Java中定义个三元数组有多么令人恶心,并且让人难以直观理解:ArrayList,并且Java还不支持元组的表达。Scala不仅有函数式语言对集合处理的先天优势:map, fold, zip, foreach, collect, filter等等,还有OOP面向对象语言的辅助函数(比如take(5)可以取得前五个元素,takeRight(5)是最后五个),这点上完虐Lisp或者Haskell这些也许在函数式表达上胜过Scala,但在简单省事以及有工业产出(Industrialized)能力上完全比不上Scala的语言。Scala对集合预制的辅助方法(Helper functions)数量之多甚至超过了Java。同时,Scala还提供immutable(不可变)结构与mutable(可变)结构,让程序员可以在命令式与函数式中自由切换。
2. 物以类聚,人以群分。函数式语言以吸引天才闻名,一直作为“学院派”语言延存至今,麻省理工至今还有一门课专门教Lisp,斯坦福每学期邀请业界的Haskell高手前去讲课。四十年前,最火的研究项目是人工智能,很多聪明人进入了这个领域,所以Lisp成为了人工智能的程序语言首选。如今最火的领域就是大数据,以及数据处理,很多聪明人进入这个领域,他们都在一段时间内被Scala吸引,所以自然就选择Scala作为他们的语言,这和Scala本身适不适合大数据其实并没有太大关系,就跟函数式语言本不擅长从事IO开发(一句著名的谚语说,IO就像一个恐怖的黑盒子,总有一种方法让函数式的美妙失灵),但还有人用Haskell开发服务器一样,对于这些天才而言,你给一袋面粉他们都能造个原子弹,让Scala作为大数据开发语言,完全不是问题。
3. 文章最前面我批评了函数式语言或者Scala的速度问题。但这其实不是问题。当C语言出来的时候,编译器水平并不高,无数写汇编的程序员纷纷斥责C语言速度缓慢,说C语言程序员不是真程序员。当年Java出来时,编译后的程序运行速度也非常慢,一直到Java1.3这个问题才有所好转。实际上,汇编语言的速度 > C语言速度 > Java速度。从这条线可以很明显的看出,越高级的语言速度越慢,Scala作为明显的比Java在进化树上爬的更高的语言,似乎有效率损失或内存管理问题是完全可以被解释的。并且在目前的所有测评中,Scala的速度并不比java慢,内存占用也大致持平。
最后绕回题主的问题上。首先,Flink不是由Scala写的,Flink是由Java写的,只是提供了Scala接口(API)而已。Apache Spark确实是由Scala写的,但Spark最先是由加州伯克利实验室公布的,所以符合我所说的第二点。Scala的威力现在才要先开始显现出来,一堆新的大学实验室开始逐步普及Scala,很多高级库已经开始用Scala写就了,比如经典概率模型建立库Figaro是由Scala写的,由前哈佛大学工程与应用科学部门的副教授Avi Pfeffer发布,贝叶斯图形模型建立库Factorie由马塞诸塞大学(UMass)的Andrew McCullen发布,高效数学计算库Breeze由加州伯克利的David Hall(此人是斯坦福大学本科生)发布。可以看出,Scala必定会成为新一轮高端库的主导语言。
同时,作为普通的大数据处理,诚然Akka Actor模型并不适合直接的文档处理,但最新的Akka-Stream完全可以胜任,Akka-Stream预加载了叫做Backpressure(向后压力)的机能,能有效防止数据过快流入产生的内存过载问题,可以专门来处理数据量大的文档(我用它来处理过约4000万行的文档)。
(这段被诺铁和qiqiqi提醒后修改)Spark也只是用akka做节点控制,数据传输还是实验室的大神们自己写的。
|