都2023年了,你还不懂批处理和流处理的区别?
Spark && Flink
就目前最热的两种流计算引擎Apache Spark和Apache Flink而言,谁最终会成为No1呢?单从 "低延时" 的角度看,Spark是Micro Batching(微批式)模式,最低延迟Spark能达到0.5~2秒左右,Flink是Native Streaming(纯流式)模式,最低延时能达到微秒。很显然是相对较晚出道的 Apache Flink 后来者居上。那么为什么Apache Flink能做到如此之 "快"呢?
根本原因是Apache Flink 设计之初就认为 "批是流的特例",整个系统是Native Streaming设计,每来一条数据都能够触发计算。相对于需要靠时间来积攒数据Micro Batching模式来说,在架构上就已经占据了绝对优势。
1.场景引入分析
日常工作中,一般会先把数据储存在一张表中,然后对这张表的数据进行加工、分析。既然数据要储存在表中,就有时效性这个概念。
如果处理的是”年级别“的数据,比如人口分析、宏观经济分析,那么数据最新日期距今间隔一两周、甚至一两个月都没什么关系。
如果处理的是”天级别“的数据,比如各大网站的用户偏好分析、零售供销分析,一般晚个几天也是可以的,即 T+N 更新。
如果是”小时级别“的数据,对时效性要求就更高了,比如金融风控,涉及到资金的安全,必须有一张小时级别的数据。
那么还有没有要求更高的?当然有了,比如风险监测,网站必须有实时监测系统,一旦有攻击,就必须立刻采取措施,双十一或者周年庆的时候,各大电商平台都经历着严峻的流量考验,也必须对系统进行实时的监测。此外,网站的实时个性化推荐、搜索引擎中也对实时性有极高的要求。
在这种场景下,传统的数据处理流程——先收集数据,然后放到DB中,再取出来分析——就无法满足这么高的实时要求,因此产生了“流式计算”的处理方法。

2.流式计算与批量计算
收集数据 - 放到DB中 - 取出来分析的传统的流程,叫做批量计算,顾名思义,将数据存起来,批量进行计算。
流式计算是对数据流进行实时计算,它不是更快的批计算,可以说,是完全不同的处理思路。
通过与批量计算进行对比的方式,介绍下其原理:
(1) 与批量计算那样慢慢积累数据不同,流式计算将大量数据平摊到每个时间点上,连续地进行小批量的进行传输,数据持续流动,计算完之后就丢弃。
(2) 批量计算是维护一张表,对表进行实施各种计算逻辑。流式计算相反,是必须先定义好计算逻辑,提交到流式计算系统,这个计算作业逻辑在整个运行期间是不可更改的。
(3) 计算结果上,批量计算对一段时间内的全部数据进行计算后传输结果,流式计算是每次小批量计算后,结果可以立刻投递到在线系统,做到实时化展现。
3.总结与相关产品
流式计算流程
① 提交流计算作业;
② 等待流式数据触发流计算作业;
③ 计算结果持续不断对外写出。
流式计算特点
① 实时、低延迟;
② 无界,数据是不断无终止的;
③ 连续,计算持续进行,计算完之后数据即丢弃;
④ 数据量大,但是不十分关注存储,一旦经过处理,要么被丢弃,要么被归档存储;
⑤ 注重数据的整体价值,不过分关注个别数据;
⑥ 数据顺序颠倒,或者不完整,系统无法控制将要处理的新到达的数据元素的顺序。
流式计算特征
① 无界(Unbounded)
数据记录(record)在计算过程中不断地动态到达,与批处理不同,计算过程开始之前就知道数据大小与边界,更容易优化
② 乱序(Out-of-order)
record的原始顺序和在处理节点上的处理顺序可能不一致,shuffle过程(数据传递)也可能导致顺序改变
③ 延迟(Delay)
record的产生时间和在处理节点上的处理时间可能差别很大
流式系统相关产品
列举一下流式计算的相关产品,不具体盘点,对流式计算感兴趣可以了解一下:
Apache Storm:Twitter 开发的第一代流处理系统
Heron:Twitter 开发的第二代流处理系统
Apache Spark streaming:
Apache Flink:
Apache Kafka:linkedin开发的一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。
Apache Samza

批量和流式的区别:
1.数据处理单位:
批量计算按数据块来处理数据,每一个task接收一定大小的数据块,比如MR,map任务在处理完一个完整的数据块后(比如128M),然后将中间数据发送给reduce任务。
流式计算的上游算子处理完一条数据后,会立马发送给下游算子,所以一条数据从进入流式系统到输出结果的时间间隔较短(当然有的流式系统为了保证吞吐,也会对数据做buffer)。
这样的结果就是:批量计算往往得等任务全部跑完之后才能得到结果,而流式计算则可以实时获取最新的计算结果。
2.数据源:
批量计算通常处理的是有限数据(bound data),数据源一般采用文件系统,而流式计算通常处理无限数据(unbound data),一般采用消息队列作为数据源。
3.任务类型:
批量计算中的每个任务都是短任务,任务在处理完其负责的数据后关闭,而流式计算往往是长任务,每个work一直运行,持续接受数据源传过来的数据。
离线=批量?实时=流式?
习惯上认为离线和批量等价;实时和流式等价,但其实这种观点并不完全正确。
假设一种情况:当拥有一个非常强大的硬件系统,可以毫秒级的处理Gb级别的数据,那么批量计算也可以毫秒级得到统计结果(当然这种情况非常极端,目前不可能),那还能说它是离线计算吗?
所以说离线和实时应该指的是:数据处理的延迟;批量和流式指的是:数据处理的方式。两者并没有必然的关系。事实上Spark streaming就是采用小批量(batch)的方式来实现实时计算。
可以参考下面链接:https://www.oreilly.com/ideas/the-world-beyond-batch-streaming-101。作者是Google实时计算的负责人,里面阐述了他对批量和实时的理解,并且作者认为批量计算只是流式计算的子集,一个设计良好的流式系统完全可以替代批量系统
关于Flink的流处理与批处理
在大数据处理领域,批处理任务与流处理任务一般被认为是两种不同的任务,一个大数据框架一般会被设计为只能处理其中一种任务。
例如Storm只支持流处理任务,而MR和Spark只支持批处理任务。Spark Streaming是Spark之上支持流处理任务的子系统,看似是一个特例,其实并不是,Spark Streaming采用了一种micro-batch的架构,就把输入数据流切分成细粒度的batch,并为每个batch数据提交一个批处理Spark任务,所以Spark Streaming本质上还是基于Spark批处理系统对流式数据进行处理,和Storm等完全流式的数据处理方式是完全不同的。而Flink通过灵活的执行引擎,能够同时支持批处理任务与流处理任务。
在执行引擎这一层,流处理系统与批处理系统最大不同在于节点间的数据传输方式。对于一个流处理系统,其节点间数据传输的标准模型是:当一条数据被处理完成后,序列化到缓存中,然后立刻通过网络传输到下一个节点,由下一个节点继续处理。而对于一个批处理系统,其节点间效据传输的标准模型是:当一条数据被处理完成后,序列化到缓存中,并不会立刻通过网络传输到下一个节点,当缓存写满,就持久化到本地硬盘上,当所有数据都被处理完成后,才开始将处理后的数据通过网络传输到下一个节点。这两种数据传输模式是两个极端,对应的是流处理系统对低延迟的要求和批处理系统对高吞吐量的要求。
Flink的执行引擎采用了一种十分灵活的方式,同时支持了这两种数据传输模型。Flink以固定的缓存块为单位进行网络数据传输,用户可以通过缓存块超时值指定缓存块的传输时机。如果缓存块的超时值为0,则Flink的数据传输方式类似上文所提到流处理系统的标准模型,此时系统可以获得最低的处理延迟。如果缓存块的超时值为无限大,则Flink的数据传输方式类似上文所提到批处理系统的标准模型,此时系统可以获得最高的吞吐量。同时缓存块的超时值也可以设置为0到无限大之间的任意值。缓存块的超时阀值越小,则Flink流处理执行引擎的数据处理延迟越低,但吞吐量也会降低,反之亦然。通过调整缓存块的超时阀值,用户可根据需求灵话地权衡系统延迟和吞吐量。

Flink和Spark Streaming的本质区别:
传统的批处理方法
传统批处理方法是持续收取数据,以时间作为划分多个批次的依据,再周期性地执行批次运算。但假设需要计算每小时出现事件转换的次数,如果事件转换跨越了所定义的时间划分,传统批处理会将中介运算结果带到下一个批次进行计算;除此之外,当出现接收到的事件顺序颠倒情况下,传统批处理仍会将中介状态带到下一批次的运算结果中,这种处理方式也不尽如人意。
理想方法
第一点,要有理想方法,这个理想方法是引擎必须要有能力可以累积状态和维护状态,累积状态代表着过去历史中接收过的所有事件,会影响到输出。
第二点,时间,时间意味着引擎对于数据完整性有机制可以操控,当所有数据都完全接受到后,输出计算结果。
第三点,理想方法模型需要实时产生结果,但更重要的是采用新的持续性数据处理模型来处理实时数据,这样才最符合 continuous data 的特性。
流式处理:
流式处理简单来讲即有一个无穷无尽的数据源在持续收取数据,以代码作为数据处理的基础逻辑,数据源的数据经过代码处理后产生出结果,然后输出,这就是流式处理的基本原理。
分布式流式处理:
假设 Input Streams 有很多个使用者,每个使用者都有自己的 ID,如果计算每个使用者出现的次数,需要让同一个使用者的出现事件流到同一运算代码,这跟其他批次需要做 group by 是同样的概念,所以跟 Stream 一样需要做分区,设定相应的 key,然后让同样的 key 流到同一个 computation instance 做同样的运算。
有状态分布式流式处理:
上述代码中定义了变数 x,x 在数据处理过程中会进行读和写,在最后输出结果时,可以依据变数 x 决定输出的内容,即状态 x 会影响最终的输出结果。
这个过程中,第一个重点是先进行了状态 co-partitioned by key,同样的 key 都会流到 computation instance,与使用者出现次数的原理相同,次数即所谓的状态,这个状态一定会跟同一个 key 的事件累积在同一个 computation instance。相当于根据输入流的 key 重新分区的状态,当分区进入 stream 之后,这个 stream 会累积起来的状态也变成 co-partiton 了。
第二个重点是 embeded local state backend。有状态分散式流式处理的引擎,状态可能会累积到非常大,当 key 非常多时,状态可能就会超出单一节点的 memory 的负荷量,这时候状态必须有状态后端去维护它;在这个状态后端在正常状况下,用 in-memory 维护即可。

更多关于大数据开发、数仓、企业实战、企业数据治理相关问题的交流,可以关注我们的公众号和视频号。动动大家的小手,转发、点赞、评论起来!
