Madhukar
世界需要另外一个大数据处理系统吗?这是当我第一次听说Apache Flink的时候产生的问题。在大数据领域,我们不缺乏框架。不过我们确实缺少一个能够满足我们不同数据处理需求的集成平台。Apache Spark似乎是解决这个问题的最好框架。因此我怀疑是否需要另外一个解决相同问题的框架。
出于好奇,在过去几周我在Flink上花了点时间。起初,我看了看一些标准的例子。 他们看上去和Spark类似。因此,我对它开始的映象就是另外一个模仿Spark功能的框架。然后,随着花的时间越来越多,我发现Flink蕴含在那些表面上看上去相同的API后面的新颖的想法与Spark有很大不同。我被那些思想所吸引,因此花了更多的时间去理解和探索。
Flink许多关于内存管理,DataSet API思路都可以在Spark里面找到,它们已经被证明确实是好的想法。所以理解Flink可以帮助我们理解分布式数据处理的未来发展方向。
在这篇博客里我试图把我作为开发者对Apache Flink所有的第一印象放在一起。这些吐槽/评论存在大的偏颇,因为我花了过去的两年在Spark上,但Apache Flink却仅仅只有两三周。所以请把下面我说的当做陋见。
什么是Apache Flink?
Apache Flink是为统一不同的数据负载而设计的另一个新的大数据处理引擎。它听起来像Apache Spark?准确的说,Flink试图解决和Spark一样的问题。两者都是致力于构建运行批处理、流、交互、图处理及机器学习等平台。因为他们在功能上没有太大的区别。但是他们的实现却有很大的不同。
所以,在下面一节我会比较Spark和Flink的不同的方面。在这两个框架中一些方法是相同的,一些却大相径庭。
Apache Spark vs Apache Flink
摘要
在Spark里面,我们有RDD抽象和用于流处理的DStream。DStream内部本身也是RDD。所以我们在Spark里面组织的数据背后都是使用RDD表示。
在Flink中,我们用DataSet抽象批处理,并在流处理应用中使用DataStreams。它们听起来很像RDD和DStreams,但其实不是。他们的区别在于:
DataSet表示的是运行时的计划
Spark里的RDD运行时以Java对象表示。虽然引入了叫“钨”的项目,情况有了些变化。在Apache Flink中,Dataset表示逻辑计划,听起来很熟悉?没错,他们很像Spakr中的Dataframes。所以,在Flink中你可以像API一样作为一等公民获得经过优化器优化的Dataframe。但Spark RDD中间是不做优化的。
Flink的Dataset就像Spark执行前优化的Datafream API
Spark 1.6 加入了Dataset API,其最终会替换RDD
Dataset与DataStream是相互独立的API
Spark中所有不同的抽象如DStream,Dataframe都是基于RDD。但在Flink,Dataset和DataStream是两个建立在共同引擎上的不同的抽象。虽然他们API相似,但是你们不能把Dataset和DataStream像DStream和RDD一样合并在一起。虽然Flink朝这个方向做了一些努力,但结果还不明朗。
我们无法把DataSet和DataStreams像RDD和DStreams一样合并。
内存管理
Spark1.5之前使用Java 堆栈来缓存数据。虽然这易于项目的启动,但导致了OOM和垃圾收集暂停的问题。Spark1.5之后使用项目“钨”(tungsten)实现自定义的内存管理。
Flink从第一天起就使用自定义的内存管理。事实上朝这个方向努力也是Spark的愿景之一。 Flink不仅以自定的二进制格式存储数据,而且还直接以二进制的方式操作数据。Spark1.5开始所有的DataFrame都是直接基于项目“钨”的二级制数据进行操作。
基于JVM做自定义的内存管理会获得更好的性能,资源利用率也更高。
语言实现
Spark使用Scala写的,提供了其他语言的API,比如Java,Python及R等。
Flink使用Java实现的,也提供Scala API。
所以语言选择方面Spark比Flink更多。另外Flink Scala API,内部是由Java实现的。我认为随着越来越多的用户使用Scala API,这种情况会有所改善。我对Spark和Flink Java API了解不多,因为我转回Scala很久了。
API
Spark和Flink都是模仿Scala集合API。所以表面上看这两类API非常相似。
// Spark wordcount
object WordCount {
def main(args: Array[String]) {
val env = new SparkContext("local","wordCount")
val data = List("hi","how are you","hi")
val dataSet = env.parallelize(data)
val words = dataSet.flatMap(value => value.split("\\s+"))
val mappedWords = words.map(value => (value,1))
val sum = mappedWords.reduceByKey(_+_)
println(sum.collect())
}
}
// Flink wordcount
objectWordCount{
def main(args:Array[String]) {
val env=ExecutionEnvironment.getExecutionEnvironment
val data=List("hi","how are you","hi")
val dataSet=env.fromCollection(data)
val words=dataSet.flatMap(value=>value.split("\\s+"))
val mappedWords=words.map(value=>(value,1))
val grouped=mappedWords.groupBy(0)
val sum=grouped.sum(1)
println(sum.collect())
}
}
虽然我不清楚这是巧合还是故意的,相似的API有助于在这些框架间之间切换。集合API好像在不久的将来将成为处理数据管道的标准API。Scala之父Martin Odersky甚至也承认这个事实。
流
Apache Spark把流看作是快速的批处理, 而Apache Flink把批处理看作是流处理的一个特例。这两种方式都很有意思。下面是一些他们的区别或者说含义:
实时 vs 准实时
Apache Flink 提供事件级别的处理,也就是所谓的实时流。和Storm模型很相似。
而Spark使用迷你批处理,但不支持事件级别的粒度,这就是所谓准实时。
Spark流是更快的批处理, 而Flink将批处理当做流处理实现。
许多应用使用准实时就可以满足需求,但少数应用还是需要事件级别的实时处理。这些应用通常涉及的都是真正的流(实时流),而不是Spark准实时流。对于他们来说Flink将是非常有吸引力的选择。
历史数据和流合并的能力
以更快的批处理来处理流的好处之一就是我们可以为批处理和流使用相同的抽象。源于底层使用统一的RDD抽象,Spark对批处理和流数据合并有着出色的支持。
Flink方面,批处理和流没有使用相同的API抽象。所以尽管也有历史数据和流合并的方法,但没有Sparkt那么清晰。
在许多应用程序内部,这种合并能力很重要,在这种情况下,Spark就会取代Flink流处理而表现优异。
灵活的窗口期
由于迷你批处理本质还是批处理,所以到目前为止Spark流窗口支持还是非常有限的。你仅仅只能基于处理时间为批处理划分窗口。
与其他任何系统相比,Flink提供非常灵活的窗口系统。窗口是Flink流API主要关注点之一。它允许基于处理时间,日期,无记录(?)等定义窗口。这种灵活性使得Flink流API相对Spark API更强大。
我不清楚把这些API加入到Spark的难易程度,所以,到那时为止,和Spark流相比,Flink拥有超级窗口API。
SQL interface
到目前为止,最活跃的的Spakr库之一就是spark-sql。 Spark提供Hive类查询语言和Dataframe类DSL查询结构化数据。这是很成熟的API,在batch里头被广泛使用,不久流处理也会广泛使用。
到目前为止,Flink Table API仅支持Dataframe类DSL,并且仍是Beta版。有计划增加SQL接口,但不确定将什么时候加入框架中。
所以Spark相对Flink有比较好的SQL支持。我认为作为后来者,Flink会很快跟进。
Data Source 集成
Spark Data Source API 是Spark框架中最好的API。它把智能数据源如NoSql数据库,Parquet,ORC最为Spark的一等公民。另外,它也提供高级操作的能力,如数据源级别的谓词下推。
Flink仍然严重依赖Map/Reduce InputFormat来做数据源集成。虽然这对拉取数据足够好了,但它无法智能的使用数据源的能力。所以Flink到目前为止这块还是比较滞后。
迭代处理
Spark谈的最多的特性之一就是进行有效的机器学些。内存级的缓存和其他实现细节使得它是实现机器学习算法真正强大的平台。
虽然机器学习是一个循环数据流,但Spark使用有向无环图(DAG)表示。一般来说,没有分布式系统鼓励使用循环数据流,因为它们会变得难于理解。
但Flink采用了有别于其他方法的方法。支持运行时控制循环依赖图。因此,它使用相比DAG更有效的方法来表示机器学习算法。
我希望Spark也开始支持循环依赖图,这将使得机器学习社区极大获益。
流平台VS批处理平台
Spark源于把整个计算表示成作为文件集合的数据的运动的Map/Reduce时代。这些文件可能以数组的形式位于内存,或者就是硬盘上的物理文件。这有非常好的容错等属性。
但是Flink是一种新型系统,它把整个计算表示成数据无任何障碍流动的流处理。这种思想和新的如akka-streams反应式流系统相似。
虽然凭我有限的调查,似乎不足以判断哪种是大数据系统的未来。使用流来处理一切好像也是最近兴起的。所以从这个意义来说,Flink为我们思考大数据系统方式带来了一股新风。
成熟度
了解了所有区别之后,你产生的一个问题可能就是Flink是和Spark一样生产环境就绪的吗?我认为还没有完全就绪。有些部分如批处理已经上生产环境了,但其他的部分如流处理,table API仍然在演进。但这不是说就没有人在生产环境中使用Flink流处理了。有些勇敢者已经在用了。然而作为大众化市场工具它需要随着时间的推移变得成熟和稳定。
结论
目前,Spark相对Flink是一个更成熟更完整的框架。但Flink带来了许多如为Table自定义内存管理,提供data set API等新的有趣的思想。Spark社区了解到了并正在将其纳入囊中。所以从这个意义上来说,Flink在引领整个大数据处理到下一个阶段。所以了解Flink API和及其内部实现将有助于你在这种的新的流处理范式登录Spark之前很好的理解它。