流处理和批处理框架的异同

2019/01/23 19:00
阅读数 2.4K

分布式流处理需求日益增加,包括支付交易、社交网络、物联网(IOT)、系统监控等。业界对流处理已经有几种适用的框架来解决,下面我们来比较各流处理框架的相同点以及区别。

 

分布式流处理是对无边界数据集进行连续不断的处理、聚合和分析。它跟MapReduce一样是一种通用计算,但我们期望延迟在毫秒或者秒级别。这类系统一般采用有向无环图(DAG)。

 

DAG是任务链的图形化表示,我们用它来描述流处理作业的拓扑。如下图,数据从sources流经处理任务链到sinks。单机可以运行DAG,但本篇文章主要聚焦在多台机器上运行DAG的情况。 

关注点

当选择不同的流处理系统时,有以下几点需要注意的:

 

运行时和编程模型:平台框架提供的编程模型决定了许多特色功能,编程模型要足够处理各种应用场景。这是一个相当重要的点,后续会继续。

 

函数式原语:流处理平台应该能提供丰富的功能函数,比如,map或者filter这类易扩展、处理单条信息的函数;处理多条信息的函数aggregation;跨数据流、不易扩展的操作join。

 

状态管理:大部分应用都需要保持状态处理的逻辑。流处理平台应该提供存储、访问和更新状态信息。

 

消息传输保障:消息传输保障一般有三种:at most once,at least once和exactly once。At most once的消息传输机制是每条消息传输零次或者一次,即消息可能会丢失;A t least once意味着每条消息会进行多次传输尝试,至少一次成功,即消息传输可能重复但不会丢失;Exactly once的消息传输机制是每条消息有且只有一次,即消息传输既不会丢失也不会重复。

 

容错:流处理框架中的失败会发生在各个层次,比如,网络部分,磁盘崩溃或者节点宕机等。流处理框架应该具备从所有这种失败中恢复,并从上一个成功的状态(无脏数据)重新消费。

 

性能:延迟时间(Latency),吞吐量(Throughput)和扩展性(Scalability)是流处理应用中极其重要的指标。 

平台的成熟度和接受度:成熟的流处理框架可以提供潜在的支持,可用的库,甚至开发问答帮助。选择正确的平台会在这方面提供很大的帮助。

 

运行时和编程模型

运行时和编程模型是一个系统最重要的特质,因为它们定义了表达方式、可能的操作和将来的局限性。因此,运行时和编程模型决定了系统的能力和适用场景。

 

实现流处理系统有两种完全不同的方式:一种是称作原生流处理,意味着所有输入的记录一旦到达即会一个接着一个进行处理。 

第二种称为微批处理。把输入的数据按照某种预先定义的时间间隔(典型的是几秒钟)分成短小的批量数据,流经流处理系统。 

两种方法都有其先天的优势和不足。

 

首先以原生流处理开始,原生流处理的优势在于它的表达方式。数据一旦到达立即处理,这些系统的延迟性远比其它微批处理要好。除了延迟性外,原生流处理的状态操作也容易实现,后续将详细讲解。

 

一般原生流处理系统为了达到低延迟和容错性会花费比较大的成本,因为它需要考虑每条记录。原生流处理的负载均衡也是个问题。比如,我们处理的数据按key分区,如果分区的某个key是资源密集型,那这个分区很容易成为作业的瓶颈。

 

接下来看下微批处理。将流式计算分解成一系列短小的批处理作业,也不可避免的减弱系统的表达力。像状态管理或者join等操作的实现会变的困难,因为微批处理系统必须操作整个批量数据。并且,batch interval会连接两个不易连接的事情:基础属性和业务逻辑。

 

相反地,微批处理系统的容错性和负载均衡实现起来非常简单,因为微批处理系统仅发送每批数据到一个worker节点上,如果一些数据出错那就使用其它副本。微批处理系统很容易建立在原生流处理系统之上。

 

编程模型一般分为组合式和声明式。组合式编程提供基本的构建模块,它们必须紧密结合来创建拓扑。新的组件经常以接口的方式完成。相对应地,声明式API操作是定义的高阶函数。它允许我们用抽象类型和方法来写函数代码,并且系统创建拓扑和优化拓扑。声明式API经常也提供更多高级的操作(比如,窗口函数或者状态管理)。后面很快会给出样例代码。

 

主流流处理系统

有一系列各种实现的流处理框架,不能一一列举,这里仅选出主流的流处理解决方案,并且支持Scala API。因此,我们将详细介绍Apache Storm,Trident,Spark Streaming,Samza和Apache Flink。前面选择讲述的虽然都是流处理系统,但它们实现的方法包含了各种不同的挑战。这里暂时不讲商业的系统,比如Google MillWheel或者Amazon Kinesis,也不会涉及很少使用的Intel GearPump或者Apache Apex。 

Apache Storm最开始是由Nathan Marz和他的团队于2010年在数据分析公司BackType开发的,后来BackType公司被Twitter收购,接着Twitter开源Storm并在2014年成为Apache较高级项目。毋庸置疑,Storm成为大规模流数据处理的先锋,并逐渐成为工业标准。Storm是原生的流处理系统,提供low-level的API。Storm使用Thrift来定义topology和支持多语言协议,使得我们可以使用大部分编程语言开发,Scala自然包括在内。

 

Trident是对Storm的一个更高层次的抽象,Trident较大的特点以batch的形式进行流处理。Trident简化topology构建过程,增加了窗口操作、聚合操作或者状态管理等高级操作,这些在Storm中并不支持。相对应于Storm的At most once流传输机制,Trident提供了Exactly once传输机制。Trident支持Java,Clojure和Scala。

 

当前Spark是非常受欢迎的批处理框架,包含Spark SQL,MLlib和Spark Streaming。Spark的运行时是建立在批处理之上,因此后续加入的Spark Streaming也依赖于批处理,实现了微批处理。接收器把输入数据流分成短小批处理,并以类似Spark作业的方式处理微批处理。Spark Streaming提供高级声明式API(支持Scala,Java和Python)。

 

Samza最开始是专为LinkedIn公司开发的流处理解决方案,并和LinkedIn的Kafka一起贡献给社区,现已成为基础设施的关键部分。Samza的构建严重依赖于基于log的Kafka,两者紧密耦合。Samza提供组合式API,当然也支持Scala。

 

最后来介绍Apache Flink。Flink是个相当早的项目,开始于2008年,但只在最近才得到注意。Flink是原生的流处理系统,提供high level的API。Flink也提供API来像Spark一样进行批处理,但两者处理的基础是完全不同的。Flink把批处理当作流处理中的一种特殊情况。在Flink中,所有的数据都看作流,是一种很好的抽象,因为这更接近于现实世界。

 

快速的介绍流处理系统之后,让我们以下面的表格来更好清晰的展示它们之间的不同: 

Word Count

Wordcount之于流处理框架学习,就好比hello world之于编程语言学习。它能很好的展示各流处理框架的不同之处,让我们从Storm开始看看如何实现Wordcount:

 

 TopologyBuilder builder = new TopologyBuilder();

 builder.setSpout("spout", new RandomSentenceSpout(), 5);

 builder.setBolt("split", new Split(), 8).shuffleGrouping("spout");

 builder.setBolt("count", new WordCount(), 12).fieldsGrouping("split", new Fields("word"));

 ...

 Map counts = new HashMap();

 public void execute(Tuple tuple, BasicOutputCollector collector) {

   String word = tuple.getString(0);

   Integer count = counts.containsKey(word) ? counts.get(word) + 1 : 1;

   counts.put(word, count);

   collector.emit(new Values(word, count));

 }

首先,定义topology。第二行代码定义一个spout,作为数据源。然后是一个处理组件bolt,分割文本为单词。接着,定义另一个bolt来计算单词数(第四行代码)。也可以看到魔数5,8和12,这些是并行度,定义集群每个组件执行的独立线程数。第八行到十五行是实际的WordCount bolt实现。因为Storm不支持内建的状态管理,所有这里定义了一个局部状态。

 

按之前描述,Trident是对Storm的一个更高层次的抽象,Trident较大的特点以batch的形式进行流处理。除了其它优势,Trident提供了状态管理,这对wordcount实现非常有用。

 

public static StormTopology buildTopology(LocalDRPC drpc) {

 FixedBatchSpout spout = ...

 TridentTopology topology = new TridentTopology();

 TridentState wordCounts = topology.newStream("spout1", spout)

 .each(new Fields("sentence"),new Split(), new Fields("word"))

 .groupBy(new Fields("word"))

 .persistentAggregate(new MemoryMapState.Factory(), 

 new Count(), new Fields("count"));

 ...

 }

如你所见,上面代码使用higher level操作,比如each(第七行代码)和groupby(第八行代码)。并且使用Trident管理状态来存储单词数(第九行代码)。

 

下面是时候祭出提供声明式API的Apache Spark。记住,相对于前面的例子,这些代码相当简单,几乎没有冗余代码。下面是简单的流式计算单词数:

 

val conf = new SparkConf().setAppName("wordcount")

val ssc = new StreamingContext(conf, Seconds(1))

val text = ...

val counts = text.flatMap(line => line.split(" "))

 .map(word => (word, 1))

 .reduceByKey(_ + _)

counts.print()

ssc.start()

ssc.awaitTermination()

每个Spark Streaming的作业都要有StreamingContext,它是流式函数的入口。StreamingContext加载第一行代码定义的配置conf,但更重要地,第二行代码定义batch interval(这里设置为1秒)。第六行到八行代码是整个单词数计算。这些是标准的函数式代码,Spark定义topology并且分布式执行。第十二行代码是每个Spark Streaming作业最后的部分:启动计算。记住,Spark Streaming作业一旦启动即不可修改。 

接下来看下Apache Samza,另外一个组合式API例子:

 

class WordCountTask extends StreamTask {

  override def process(envelope: IncomingMessageEnvelope, collector: MessageCollector, 

    coordinator: TaskCoordinator) {

    val text = envelope.getMessage.asInstanceOf[String]

    val counts = text.split(" ").foldLeft(Map.empty[String, Int]) {

      (count, word) => count + (word -> (count.getOrElse(word, 0) + 1))

    }

    collector.send(new OutgoingMessageEnvelope(new SystemStream("kafka", "wordcount"), counts))

 }

Samza的属性配置文件定义topology,为了简明这里并没把配置文件放上来。定义任务的输入和输出,并通过Kafka topic通信。在单词数计算整个topology是WordCountTask。在Samza中,实现特殊接口定义组件StreamTask,在第三行代码重写方法process。它的参数列表包含所有连接其它系统的需要。第八行到十行简单的Scala代码是计算本身。 

 

Flink的API跟Spark Streaming是惊人的相似,但注意到代码里并未设置batch interval。

 

val env = ExecutionEnvironment.getExecutionEnvironment

 val text = env.fromElements(...)

 val counts = text.flatMap ( _.split(" ") )

   .map ( (_, 1) )

   .groupBy(0)

   .sum(1)

 counts.print()

 env.execute("wordcount")

上面的代码是相当的直白,仅仅只是几个函数式调用,Flink支持分布式计算。

 

容错性

流处理系统的容错性与生俱来的比批处理系统难实现。当批处理系统中出现错误时,我们只需要把失败的部分简单重启即可;但对于流处理系统,出现错误就很难恢复。因为线上许多作业都是7 x 24小时运行,不断有输入的数据。流处理系统面临的另外一个挑战是状态一致性,因为重启后会出现重复数据,并且不是所有的状态操作是幂等的。容错性这么难实现,那下面我们看看各大主流流处理框架是如何处理这一问题。

 

Apache Storm:Storm使用上游数据备份和消息确认的机制来保障消息在失败之后会重新处理。消息确认原理:每个操作都会把前一次的操作处理消息的确认信息返回。Topology的数据源备份它生成的所有数据记录。当所有数据记录的处理确认信息收到,备份即会被安全拆除。失败后,如果不是所有的消息处理确认信息收到,那数据记录会被数据源数据替换。这保障了没有数据丢失,但数据结果会有重复,这就是at-least once传输机制。

 

Storm采用取巧的办法完成了容错性,对每个源数据记录仅仅要求几个字节存储空间来跟踪确认消息。纯数据记录消息确认架构,尽管性能不错,但不能保证exactly once消息传输机制,所有应用开发者需要处理重复数据。Storm存在低吞吐量和流控问题,因为消息确认机制在反压下经常误认为失败。 

Spark Streaming:Spark Streaming实现微批处理,容错机制的实现跟Storm不一样的方法。微批处理的想法相当简单。Spark在集群各worker节点上处理micro-batches。每个micro-batches一旦失败,重新计算就行。因为micro-batches本身的不可变性,并且每个micro-batches也会持久化,所以exactly once传输机制很容易实现。 

Samza:Samza的实现方法跟前面两种流处理框架完全不一样。Samza利用消息系统Kafka的持久化和偏移量。Samza监控任务的偏移量,当任务处理完消息,相应的偏移量被移除。消息的偏移量会被checkpoint到持久化存储中,并在失败时恢复。但是问题在于:从上次checkpoint中修复偏移量时并不知道上游消息已经被处理过,这就会造成重复。这就是at least once传输机制。 

Apache Flink:Flink的容错机制是基于分布式快照实现的,这些快照会保存流处理作业的状态(本文对Flink的检查点和快照不进行区分,因为两者实际是同一个事物的两种不同叫法。Flink构建这些快照的机制可以被描述成分布式数据流的轻量级异步快照,它采用Chandy-Lamport算法实现。)。

 

如果发生失败的情况,系统可以从这些检查点进行恢复。Flink发送checkpoint的栅栏(barrier)到数据流中(栅栏是Flink的分布式快照机制中一个核心的元素),当checkpoint的栅栏到达其中一个operator,operator会接所有收输入流中对应的栅栏(比如,图中checkpoint n对应栅栏n到n-1的所有输入流,其仅仅是整个输入流的一部分)。

 

所以相对于Storm,Flink的容错机制更高效,因为Flink的操作是对小批量数据而不是每条数据记录。但也不要让自己糊涂了,Flink仍然是原生流处理框架,它与Spark Streaming在概念上就完全不同。Flink也提供exactly once消息传输机制。 

状态管理

大部分大型流处理应用都涉及到状态。相对于无状态的操作(其只有一个输入数据,处理过程和输出结果),有状态的应用会有一个输入数据和一个状态信息,然后处理过程,接着输出结果和修改状态信息。

 

因此,我们不得不管理状态信息,并持久化。我们期望一旦因某种原因失败,状态能够修复。状态修复有可能会出现小问题,它并不总是保证exactly once,有时也会出现消费多次,但这并不是我们想要的。

 

据我们所知,Storm提供at-least once的消息传输保障。那我们又该如何使用Trident做到exactly once的语义。概念上貌似挺简单,你只需要提交每条数据记录,但这显然不是那么高效。所以你会想到小批量的数据记录一起提交会优化。Trident定义了几个抽象来达到exactly once的语义,见下图,其中也会有些局限。 

Spark Streaming是微批处理系统,它把状态信息也看做是一种微批量数据流。在处理每个微批量数据时,Spark加载当前的状态信息,接着通过函数操作获得处理后的微批量数据结果并修改加载过的状态信息。 

Samza实现状态管理是通过Kafka来处理的。Samza有真实的状态操作,所以其任务会持有一个状态信息,并把状态改变的日志推送到Kafka。如果需要状态重建,可以很容易的从Kafka的topic重建。为了达到更快的状态管理,Samza也支持把状态信息放入本地key-value存储中,所以状态信息不必一直在Kafka中管理,见下图。不幸的是,Samza只提供at-least once语义,exactly once的支持也在计划中。 

Flink提供状态操作,和Samza类似。Flink提供两种类型的状态:一种是用户自定义状态;另外一种是窗口状态。如图,第一个状态是自定义状态,它和其它的的状态不相互作用。这些状态可以分区或者使用嵌入式Key-Value存储状态[文档一和二]。当然Flink提供exactly-once语义。下图展示Flink长期运行的三个状态。 

 

单词计数例子中的状态管理

单词计数的详细代码见上篇文章,这里仅关注状态管理部分。

 

让我们先看Trident:

 

public static StormTopology buildTopology(LocalDRPC drpc) {

   FixedBatchSpout spout = ...

   TridentTopology topology = new TridentTopology();

   TridentState wordCounts = topology.newStream("spout1", spout)

     .each(new Fields("sentence"),new Split(), new Fields("word"))

     .groupBy(new Fields("word"))

     .persistentAggregate(new MemoryMapState.Factory(), new Count(), new Fields("count"));

 ...

 }

在第九行代码中,我们通过调用persistentAggregate创建一个状态。其中参数Count存储单词数,如果你想从状态中处理数据,你必须创建一个数据流。从代码中也可以看出实现起来不方便。

 

Spark Streaming声明式的方法稍微好点:

 

// Initial RDD input to updateStateByKey

val initialRDD = ssc.sparkContext.parallelize(List.empty[(String, Int)])

val lines = ...

val words = lines.flatMap(_.split(" "))

val wordDstream = words.map(x => (x, 1))

val trackStateFunc = (batchTime: Time, word: String, one: Option[Int], 

  state: State[Int]) => {

    val sum = one.getOrElse(0) + state.getOption.getOrElse(0)

    val output = (word, sum)

    state.update(sum)

    Some(output)

  }

val stateDstream = wordDstream.trackStateByKey(

  StateSpec.function(trackStateFunc).initialState(initialRDD))

首先我们需要创建一个RDD来初始化状态(第二行代码),然后进行transformations(第五行和六行代码)。接着在第八行到十四行代码,我们定义函数来处理单词数状态。函数计算并更新状态,最后返回结果。第十六行和十七行代码,我们得到一个状态信息流,其中包含单词数。

 

接着我们看下Samza:

 

class WordCountTask extends StreamTask with InitableTask {

  private var store: CountStore = _

  def init(config: Config, context: TaskContext) {

    this.store = context.getStore("wordcount-store")

      .asInstanceOf[KeyValueStore[String, Integer]]

  }

 override def process(envelope: IncomingMessageEnvelope,

   collector: MessageCollector, coordinator: TaskCoordinator) {

   val words = envelope.getMessage.asInstanceOf[String].split(" ")

   words.foreach { key =>

     val count: Integer = Option(store.get(key)).getOrElse(0)

     store.put(key, count + 1)

     collector.send(new OutgoingMessageEnvelope(new SystemStream("kafka", "wordcount"), 

       (key, count)))

   }

 }

首先在第三行代码定义状态,进行Key-Value存储,在第五行到八行代码初始化状态。接着在计算中使用,上面的代码已经很直白。

 

最后,讲下Flink使用简洁的API实现状态管理:

 

val env = ExecutionEnvironment.getExecutionEnvironment

val text = env.fromElements(...)

val words = text.flatMap ( _.split(" ") )

words.keyBy(x => x).mapWithState {

  (word, count: Option[Int]) =>

    {

      val newCount = count.getOrElse(0) + 1

      val output = (word, newCount)

      (output, Some(newCount))

    }

}

我们仅仅需要在第六行代码中调用mapwithstate函数,它有一个函数参数(函数有两个变量,第一个是单词,第二个是状态。然后返回处理的结果和新的状态)。

 

流处理框架性能

这里所讲的性能主要涉及到的是延迟性和吞吐量。

 

对于延迟性来说,微批处理一般在秒级别,大部分原生流处理在百毫秒以下,调优的情况下Storm可以很轻松的达到十毫秒。

 

同时也要记住,消息传输机制保障,容错性和状态恢复都会占用机器资源。例如,打开容错恢复可能会降低10%到15%的性能,Storm可能降低70%的吞吐量。总之,天下没有免费的午餐。对于有状态管理,Flink会降低25%的性能,Spark Streaming降低50%的性能。

 

也要记住,各大流处理框架的所有操作都是分布式的,通过网络发送数据是相当耗时的,所以进了利用数据本地性,也尽量优化你的应用的序列化。

 

项目成熟度

当你为应用选型时一定会考虑项目的成熟度。下面来快速浏览一下: 

Storm是第一个主流的流处理框架,后期已经成为长期的工业级的标准,并在像Twitter,Yahoo,Spotify等大公司使用。

 

Spark Streaming是最近最流行的Scala代码实现的流处理框架。现在Spark Streaming被公司(Netflix, Cisco, DataStax, Intel, IBM等)日渐接受。

 

Samza主要在LinkedIn公司使用。Flink是一个新兴的项目,很有前景。

 

你可能对项目的贡献者数量也感兴趣。Storm和Trident大概有180个代码贡献者;整个Spark有720多个;根据github显示,Samza有40个;Flink有超过130个代码贡献者。

 

流处理框架推荐

应用选型是大家都会遇到的问题,一般是根据应用具体的场景来选择特定的流处理框架。下面给出几个作者认为优先考虑的点:

 

High level API:具有high level API的流处理框架会更简洁和高效;

 

状态管理:大部分流处理应用都涉及到状态管理,因此你得把状态管理作为评价指标之一;

 

exactly once语义:exactly once会使得应用开发变得简单,但也要看具体需求,可能at least once 或者at most once语义就满足你得要求;

 

自动恢复:确保流处理系统能够快速恢复,你可以使用Chaos Monkey或者类似的工具进行测试。快速的恢复是流处理重要的部分。

 

Storm:Storm非常适合任务量小但速度要求高的应用。如果你主要在意流处理框架的延迟性,Storm将可能是你的首先。但同时也要记住,Storm的容错恢复或者Trident的状态管理都会降低整体的性能水平。也有一个潜在的Storm更新项目-Twitter的Heron,Heron设计的初衷是为了替代Storm,并在每个单任务上做了优化但同时保留了API。

 

Spark Streaming:如果你得基础架构中已经设计到Spark,那Spark Streaming无疑是值得你尝试的。因为你可以很好的利用Spark各种library。如果你需要使用Lambda架构,Spark Streaming也是一个不错的选择。但你要时刻记住微批处理的局限性,以及它的延迟性问题。

 

Samza:如果你想使用Samza,那Kafka应该是你基础架构中的基石,好在现在Kafka已经成为家喻户晓的组件。像前面提到的,Samza一般会搭配强大的本地存储一起,这对管理大数据量的状态非常有益。它可以轻松处理上万千兆字节的状态信息,但要记住Samza只支持at least once语义。

 

Flink:Flink流处理系统的概念非常不错,并且满足绝大多数流处理场景,也经常提供前沿的功能函数,比如,高级窗口函数或者时间处理功能,这些在其它流处理框架中是没有的。同时Flink也有API提供给通用的批处理场景。但你需要足够的勇气去上线一个新兴的项目,并且你也不能忘了看下Flink的roadmap。

 

Dataflow和开源

最后,我们来聊下Dataflow和它的开源。Dataflow是Google云平台的一部分,Google云平台包含很多组件:大数据存储,BigQuery,Cloud PubSub,数据分析工具和前面提到的Dataflow。

 

Dataflow是Google管理批处理和流处理的统一API。它是建立在MapReduce(批处理),FlumeJava(编程模型)和MillWheel(流处理)之上。Google最近决定开源Dataflow SDK,并完成Spark和Flink的runner。现在可以通过Dataflow的API来定义Google云平台作业、Flink作业或者Spark作业,后续会增加对其它引擎的支持。

 

Google为Dataflow提供Java、Python的API,社区已经完成Scalable的DSL支持。除此之外,Google及其合作者提交Apache Beam到Apache。 

结论

本系列文章粗略的讲述各大流行的流处理框架,并讨论了它们的相似性、区别、折衷权衡和使用的场景。希望这些将会给你设计流处理方案有帮助。

 

Flink与storm的主要区别译文

Qestion:

Flink被用来和Spark相比,但是我认为这样的比较不太合适,把Flink窗口事件和Spark微批处理进行比较,同样的Flink与Samza对比也是,这两种情况下的比较都是实时流计算与批量处理事件策略的比较,我更想比较Flink与Storm之间的区别,这两者在概念上更相近。

我发现了这个幻灯片1(4),他主要的区别在于“可调整延迟时间”,在Slicon Angle的文章中一些暗示,flink更好的集成了spark与HadoopMr,但是没有实际确切的细节与提及。在最近的一场分享上 Fabian Hueske说“和Storm相比,flink提供了更高级的api,和一种更加轻量级的容错策略来提供exactly-once语义得到确切执行”。

上面提到的,我对这方面了解比较少?我不是能十分理解这些观点,可以解释一下这些问题吗?流在storm中是怎么处理的?在flink中又是怎么解决的? Hueske 所说的更高级的api是什么?更轻量级的容错策略又是什么?

Answer1:

声明:我是Apache Flink 项目代码的提交者和PMC 成员,我只对Storm高层架构熟悉,对其底层实现不熟悉。

Apache Flink是一个统一流处理与批处理的框架。由于流水线数据在并行任务之间进行传输(包括数据的洗牌shuffles),flink在运行时支持流处理与批处理。数据被立刻的传输从生产数据的任务到接受数据的任务(在网络传输中被收集在一个缓存中,然后发送)之后,批处理的任务可以被选择来处理这些阻塞的数据。

Apache Spark 也是一个处理流和批处理的框架,Flink 的批处理api用法案列和Spark 非常相似,但是内部实现不一样。对于处理流来说,两种框架采用了不同的实现使得他们适合于不同场景的应用(Spark 微批处理 vs Flink  流计算),我可以说Spark与Flink的比较是有用有效的。然而Spark却和Flink 不是最相似的流处理引擎。

回到原来的问题。Apache Storm 只能处理流数据,没有批处理的能力。事实上,Flink的流式处理引擎和Storm很相似,比如 Flink的并行任务和Storm的bolt很相似。他俩都是通过流水线数据的传输来降低数据延时。然而Flink 提供了很多跟高级的api ,Flink的DataStream提供了Map、GroupBy、Window和Join等api来代替storm的bolt在一个或多个readers 和collectors的功能,而Storm在实现这些功能的时候都需要程序员自己实现。另外的不同在于处理语义。Storm提供了"at-least-once "而Flink提供了"exactly-once",两个框架给与语义不同的保证在实现上也就相当的不同。Storm 采用 record-level ack,Flink采用Chandy-Lamport的轻微变种。简言之,在数据源中周期性的注入marker,然后放入数据流中,无论何时只要任务执行器接受到一个marker,执行器检查他的内部状态。当一个marker被所有的数据输出sink给接收到,证明这个marker被提交(在这个marker之前的所有执行的数据,到上一个被提交的marker之后的所以数据)。万一有一个sink没有接受到marker,所有的源操作器将从置他们的处理数据到最近一次确认提交的marker然后继续执行。这种检查标记点的方法比record-level ack更加的轻量级。这个幻灯片(幻灯片2)讨论了flink相应的容错机制、检查点、处理状态。

Storm也提供了 exactly-once 以及高级api ,但是被称为Trident ,然而Trident 是在微批处理的基础上实现的,就很像Spark了而不是Flink

Flink的“可调延迟性”指的是Flink的记录从一个任务到另外一个任务的时间延迟。我再前面说了,flink使用流水线转移,在数据生成之后快速转发。为了效率,这些记录会被收集起来放在缓冲器中,当缓冲器满了或者是到了一个确定的阈值时间点被网络发送。这个阈值控制着这个记录的延迟。因为他指定了最大时间的延迟,一个记录从生成到发送出去。然而,他不能被用来保证一个数据从进入Flink项目到出Flink项目所花费的时间,因为这取决于任务处理执行时间和网络传输了多少次。

Answer2:

添加到上一条回答;

Flink比Storm的改进还有以下几点:

背压:Flink流计算当不同操作器在速度不同的时候表现的很好。因为低速流操作器背压高速流操作器很好,虽然网络层管理控制缓存池。

用户定义状态:Flink允许用户在操作器中自定义状态。这个自定义状态可以参与在检查点的容错处理,也提供exactly-once语义支持。参见这个列子(幻灯片3),在一个操作器中用户自定义机器状态,该状态始终与数据流一起参与checkpoint。

流窗口:流窗口和窗口聚合是数据流分析的重要组成部分。Flink配备了一个非常强大的窗口系统,支持多种类型的窗口。

 

译文:

https://stackoverflow.com/questions/30699119/what-is-are-the-main-differences-between-flink-and-storm

幻灯片1:

https://www.slideshare.net/GyulaFra/flink-streaming-43445818

幻灯片2:

https://www.slideshare.net/stephanewen1/flink-history-roadmap-and-vision

https://www.youtube.com/watch?v=fw2DBE6ZiEQ

幻灯片3:

https://github.com/StephanEwen/flink-demos/tree/master/streaming-state-machine

stackoverflow:the difference between spark and flink

https://stackoverflow.com/questions/28082581/what-is-the-difference-between-apache-spark-and-apache-flink

大数据处理引擎Spark与Flink对比分析

大数据技术正飞速地发展着,催生出一代又一代快速便捷的大数据处理引擎,无论是Hadoop、Storm,还是后来的Spark、Flink。然而,毕竟没有哪一个框架可以完全支持所有的应用场景,也就说明不可能有任何一个框架可以完全取代另一个。我们将从几个项出发着重对比Spark与Flink这两个大数据处理引擎,探讨其两者的区别。

 

  一、Spark与Flink几个主要项目的对比与分析

  1.性能对比

  测试环境:

  CPU:7000个

  内存:128GB

  版本:Hadoop 2.3.0,Spark 1.4,Flink 0.9

  数据:800MB,8GB,8TB

  算法:K-means:以空间中K个点为中心进行聚类,对最靠近它们的对象归类,通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果

  迭代:K=10,3组数据

  相同点:Spark与Flink都运行在Hadoop YARN上,两者都拥有非常好的计算性能,因为两者都可以基于内存计算框架以进行实时计算。

  相异点:结合上图三者的迭代次数(纵坐标是秒,横坐标是次数)图表观察,可得出在性能上,呈现Flink > Spark > Hadoop(MR)的结果,且迭代次数越多越明显。Flink之所以优于Spark和Hadoop,最主要的原因是Flink支持增量迭代,具有对迭代自动优化的功能。

  结果:Flink胜。

  2.流式计算比较

  相同点:Spark与Flink都支持流式计算。

  相异点:Spark是基于数据片集合(RDD)进行小批量处理的,它只能支持秒级计算,所以Spark在流式处理方面,不可避免会增加一些延时。Flink是一行一行的,它的流式计算跟Storm的性能差不多,是支持毫秒级计算的。

  结果:Flink胜。

  3.与Hadoop兼容性对比

  相同点:Spark与Flink的数据存取都支持HDFS、HBase等数据源,而且,它们的计算资源调度都支持YARN的方式。

  相异点:Spark不支持TableMapper和TableReducer这些方法。Flink对Hadoop有着更好的兼容,如可以支持原生HBase的TableMapper和TableReducer,唯一不足是新版本的MapReduce方法无法得到支持,现在只支持老版本的MapReduce方法。

  结果:Flink胜。

  4.SQL支持对比

  相同点:两者都支持SQL。

  相异点:从范围上说,Spark对SQL的支持比Flink的要大一些,而且Spark支持对SQL的优化(包括代码生成和快速Join操作),还要提供对SQL语句的扩展和更好地集成。Flink主要支持对API级的优化。

  结果:Spark胜。

  5.计算迭代对比

  相同点:如下图所示,Hadoop(MR)、Spark和Flink均能迭代。

  相异点:Flink特有delta-iterations,这让它能够在迭代中显著减少计算。并且Flink具有自动优化迭代程序功能,具体流程如下图所示。

  结果:Flink胜。

  6.社区支持对比

  相同点:Spark与Flink均有社区支持。

  相异点:Spark社区活跃度比Flink高很多。

  结果:Spark胜。

  二、Spark与Flink的特点剖析

  1.Spark 1.4的6大特点

  众所周知,提出最主要抽象概念——弹性分布式数据集(RDD)的是Spark。RDD是一个元素集合,将其划分到集群的各个节点上可以被并行操作。当然,用户也可以让Spark保留一个RDD在内存里,让其能在并行操作中被有效地重复使用。Spark是实至名归的快速、通用的计算集群系统。结合下图Spark架构图与生态系统图,可以看出Spark 1.4的6大特点:

 

  ①Spark SQL(DataFrame)添加了ORCFile类型支持以及所有的Hive metastore支持;

  ②增加了UI的Spark Streaming,使得用户查看各种状态更加地便捷,随着和Kafka融合的加深,对Kinesis的支持也加强了很多;

  ③Spark之所以提供了更多的算法和工具,是因为Spark ML/MLlib的ML pipelines越来越成熟;

  ④使用了REST API,Spark可以为应用获取如jobs、stages、storage info、tasks等各种信息;

  ⑤内存管理、代码生成、垃圾回收等方面都有很多改进,这些都得益于Tungsten项目的持续优化;

  ⑥SparkR的发布让Spark得到更友好的R语法的支持。

  2.Flink 0.9的7大特点

  作为可扩展的批处理和流式数据处理的数据处理平台,Flink的设计思想主要来源于Hadoop、MPP数据库、流式计算系统等。支持增量迭代计算是Flink最大的特点,而且其对于迭代计算和流式计算的支持力度都将会加强。结合下图Flink架构图与生态系统图,可以看出Flink 0.9的7大特点:

  ①搭载DataSet API,让Flink支持Java、Python和Scala等多种编程语言;

  ②同样地,搭载DataStream API,让Flink支持Java和Scala;

  ③Flink ML和Gelly提供机器学习和图处理的多种库;

  ④Table API能够支持类SQL;

  ⑤Flink能够支持高效序列化、反序列化;

  ⑥Flink和Hadoop相互兼容;

  ⑦Flink拥有自动优化迭代的功能。

  放眼未来,无论是Spark还是Flink,两者的发展重点都将是数据科学和平台API化,使其生态系统越来越完善。亦或许,会有更新的大数据处理引擎出现,谁知道呢。

Spark提出的最主要抽象概念是弹性分布式数据集(RDD)

 

flink支持增量迭代计算。基于流执行引擎,Flink提供了诸多更高抽象层的API以方便用户编写分布式任务:

 

1. DataSet API, 对静态数据进行批处理操作,将静态数据抽象成分布式的数据集,用户可以方便的采用Flink提供的各种操作符对分布式数据集进行各种操作,支持Java,Scala和Python。

2. DataStream API,对数据流进行流处理操作,将流式的数据抽象成分布式的数据流,用户可以方便的采用Flink提供的各种操作符对分布式数据流进行各种操作,支持Java和Scala。

3. Table API,对结构化数据进行查询操作,将结构化数据抽象成关系表,并通过Flink提供的类SQL的DSL对关系表进行各种查询操作,支持Java和Scala。

此外,Flink还针对特定的应用领域提供了领域库,例如:

1. Flink ML,Flink的机器学习库,提供了机器学习Pipelines API以及很多的机器学习算法实现。

2. Gelly,Flink的图计算库,提供了图计算的相关API以及很多的图计算算法实现。

 

Spark Streaming是Apache Spark之上支持流处理任务的子系统,看似一个特例,实则不然。Spark Streaming采用了一种micro-batch的架构,即将输入的数据流切分成细粒度的batch数据,对于每一个batch数据,以此为输入提交一个批处理Spark任务,所以Spark Streaming本质上还是基于Spark批处理系统对流式数据进行处理,和Apache Storm,Apache Smaza等完全流式的数据处理方式完全不同。Flink能够同时处理批处理任务与流处理任务,其灵活的执行引擎支持完全原生的批量的数据处理和流式的数据处理。

在执行引擎这一层,流处理系统与批处理系统最大的不同在于节点间数据传输的方式。对于一个流处理系统,其节点间数据传输的标准模型是:当一条数据被处理完成后,序列化到缓存中,然后立刻通过网络传输到下一个节点,由下一个节点继续处理。而对于一个批处理系统,其节点间数据传输的标准模型是:当一条数据被处理完成后,序列化到缓存中,并不会立刻通过网络传输到下一个节点,当缓存写满,就持久化到本地硬盘上,当所有数据都被处理完成后,才开始将处理后的数据通过网络传输到下一个节点。

 

 

这两种数据传输模式是两个极端,对应的是流处理系统对低延迟的要求和批处理系统对高吞吐量的要求。Flink的执行引擎采用了一种十分灵活的方式,同时支持了这两种数据传输模型。Flink以固定的缓存块为单位进行网络数据传输,用户可以通过缓存块超时值指定缓存块的传输时机。如果缓存块的超时值为0,则Flink的数据传输方式类似上面提到的流处理系统的标准模型,此时系统可以获得最低的处理延迟。如果缓存块的超时值为无限大,则Flink的数据传输方式类似上面提到的批处理系统的标准模型,此时系统可以获得最高的处理吞吐量。同时缓存块的超时值也可以设置为0到无限大之间的任意值。缓存块的超时阈值越小,则Flink流处理执行引擎的数据处理延迟越低,但吞吐量也会越低,缓存块的超时阈值越大时,则反之。通过调整缓存块的超时阈值,用户可根据自己的需要灵活的权衡Flink的延迟和吞吐量。

 

 

性能对比

首先它们都可以基于内存计算框架进行实时计算,所以都拥有非常好的计算性能。经过测试,Flink计算性能上略好。

 

测试环境:

  1. CPU:7000个;
  2. 内存:128GB;
  3. 版本:Hadoop 2.3.0,Spark 1.4,Flink 0.9
  4. 数据:800MB,8GB,8TB;
  5. 算法:K-means:以空间中K个点为中心进行聚类,对最靠近它们的对象归类。通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果。
  6. 迭代:K=10,3组数据

迭代次数(纵坐标是秒,横坐标是次数)

总结:Spark和Flink全部都运行在Hadoop YARN上,性能为Flink > Spark > Hadoop(MR),迭代次数越多越明显,性能上,Flink优于Spark和Hadoop最主要的原因是Flink支持增量迭代,具有对迭代自动优化的功能。

 

流式计算比较

它们都支持流式计算,Flink是一行一行处理,而Spark是基于数据片集合(RDD)进行小批量处理,所以Spark在流式处理方面,不可避免增加一些延时。Flink的流式计算跟Storm性能差不多,支持毫秒级计算,而Spark则只能支持秒级计算。

 

问题

谬论1:延迟和吞吐量:只能选择一个

 

早期的开源流处理框架要么是“高吞吐”的,要么是“低延迟”的,而“海量且快速”一直未能成为开源流处理框架的代名词。

不过Flink(可能还有其它的框架)就同时提供了高吞吐和低延迟。这里有一个基准测试结果的样例。

让我们从底层来剖析这个例子,特别是从硬件层,并结合具有网络瓶颈的流处理管道(很多使用Flink的管道都有这个瓶颈)。在硬件层不应该存在需要作出权衡的条件,所以网络才是影响吞吐量和延迟的主要因素。

一个设计良好的软件系统应该会充分利用网络的上限而不会引入瓶颈问题。不过对Flink来说,总是有可优化的空间,可以让它更接近硬件所能提供的效能。使用一个包含10个节点的集群,Flink现在每秒可以处理千万级别的事件量,如果扩展到1000个节点,它的延迟可以降低到几十毫秒。在我们看来,这种水平已经比很多现有的方案高出很多。

参考:

http://www.csdn.net/article/2015-07-16/2825232

http://mobile.51cto.com/adatabase-524305.htm

问:apache spark 与 flink有什么区别?apache flink是否会取代hadoop?

答:

首先它们有哪些共同点?flink和spark都是apache 软件基金会(ASF)旗下顶级项目,都是通用数据处理平台。它们可以应用在很多的大数据应用和处理环境。并且有如下扩展:

 

 

  SPARK   Flink
类SQL查询 Spark SQL MRQL
图计算 GraphX Spargel(基础)和Gelly(库)
机器学习 MLib Flink ML
流计算 Spark Streaming Flink Streaming

并且两者均可在不依赖于其他环境的情况下运行于standalone模式,或是运行在基于hadoop(YARN,HDFS)之上,由于它们均是运行于内存,所以他们表现的都比hadoop要好很多。

然而它们在实现上还是有很多不同点:

在spark 1.5.x之前的版本,数据集的大小不能大于机器的内存数。

Flink在进行集合的迭代转换时可以是循环或是迭代计算处理。这使得Join算法、对分区的链接和重用以及排序可以选择最优算法。当然flink也是一个很强大的批处理工具。flink的流式处理的是真正的流处理。流式数据一但进入就实时进行处理,这就允许流数据灵活地在操作窗口。它甚至可以在使用水印的流数中处理数据(It is even capable of handling late data in streams by the use of watermarks)。此外,flink的代码执行引擎还对现有使用storm,mapreduce等有很强的兼容性。

Spark 在另一方面是基于 弹性分布式数据集(RDD),这(主要的)给于spark基于内存内数据结构的函数式编程。它可以通过固定的内存给于大批量的计算。spark streaming 把流式数据封装成小的批处理,也就是它收集在一段时间内到达的所有数据,并在收集的数据上运行一个常规批处理程序。同时一边收集下一个小的批处理数据。

Flink 有可能会替代hadoop吗?

不会,hadoop 有以下几项功能:

1、HDFS - hadoop 分布式文件系统(hadoop Distributed Filesystem)

2、YARN - Yet Another Resource Negotiator(资源管理器)

3、MapReduce - 一个hadoop批处理框架

HDFS和YARN仍旧是大数据集群中不可缺少的一部份,这两项是部署其他分布式技术的基础,如分布式查询或分布式数据库 。MapReduce的主要用例是对数据集的批处理,而不是集群的RAM,而Flink则是为流和迭代处理而设计的。所以hadoop和flink能共存。即使如此,我还是强烈推荐更强大的,对批处理更易用的flink.

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部