在批处理场景中,我们主要通过一次计算的总耗时来评价性能。在流处理场景,数据源源不断地流入系统,大数据框架对每个数据的处理越快越好,大数据框架能处理的数据量越大越好。衡量流处理的“快”和“量”两方面的性能,一般用延迟(Latency)和吞吐(Throughput)。
1、延迟
延迟表示一个事件被系统处理的总时间,一般以毫秒为单位。根据业务不同,我们一般关心平均延迟(Average Latency)和分位延迟(Percentile Latency)。假设一个食堂的自助取餐流水线是一个流处理系统,每个就餐者前来就餐是它需要处理的事件,从就餐者到达食堂到他拿到所需菜品并付费离开的总耗时,就是这个就餐者的延迟。如果正赶上午餐高峰期,就餐者极有可能排队,这个排队时间也要算在延迟中。例如,99分位延迟表示对所有就餐者的延迟进行统计和排名,取排名第99%位的就餐者延迟。一般商业系统更关注分位延迟,因为分位延迟比平均延迟更能反映这个系统的一些潜在问题。还是以食堂的自助餐流水线为例,该流水线的平均延迟可能不高,但是在就餐高峰期,延迟一般会比较高。如果延迟过高,部分就餐者会因为等待时间过长而放弃排队,用户体验较差。通过检查各模块分位延迟,能够快速定位到哪个模块正在“拖累”整个系统的性能。
延迟对于很多流处理系统非常重要,比如欺诈检测系统、告警监控系统等。Flink可以将延迟降到毫秒级别。如果用mini-batch的思想处理同样的数据流,很可能有分钟级到小时级的延迟,因为批处理引擎必须等待一批数据达到才开始进行计算。
2、吞吐
吞吐表示一个系统最多能处理多少事件,一般以单位时间处理的事件数量为标准。需要注意的是,吞吐除了与引擎自身设计有关,也与数据源发送过来的事件数据量有关,有可能流处理引擎的最大吞吐量远小于数据源的数据量。比如,自助取餐流水线可能在午餐时间的需求最高,很可能出现大量排队的情况,但另外的时间几乎不需要排队等待。假设一天能为1 000个人提供就餐服务,共计10小时,那么它的平均吞吐量为100人/小时;仅午间2小时的高峰期就提供了600人,它的峰值吞吐量是300人/小时。比起平均吞吐量,峰值吞吐量更影响用户体验,如果峰值吞吐量低,会导致就餐者等待时间过长而放弃排队。排队的过程被称作缓存(Buffering)。如果排队期间仍然有大量数据进入缓存,很可能超出系统的极限,就会出现反压(Backpressure)问题,这时候就需要一些优雅的策略来处理类似问题,否则会造成系统崩溃,用户体验较差。
延迟与吞吐其实并不是相互孤立的,它们相互影响。如果延迟高,那么很可能吞吐较低,系统处理不了太多数据。为了优化这两个指标,首先提高自助取餐流水线的行进速度,加快取餐各个环节的进程。当用户量大到超过流水线的瓶颈时,需要再增加一个自助取餐流水线。这就是当前大数据系统都在采用的两种加速方式,第一是优化单节点内的计算速度,第二是使用并行策略,分而治之地处理数据。如果一台计算机做不了或做得不够快,那就用更多的计算机一起来做。
延迟和吞吐是衡量流处理引擎的重要指标。如何保证流处理系统保持高吞吐和低延迟是一项非常有挑战性的工作。
3、窗口
比起批处理,流处理对窗口(Window)和时间概念更为敏感。在批处理场景下,数据已经按照某个时间维度被分批次地存储了。一些公司经常将用户行为日志按天存储,一些开放数据集都会说明数据采集的时间始末。因此,对于批处理任务,处理一个数据集,其实就是对该数据集对应的时间窗口内的数据进行处理。在流处理场景下,数据以源源不断的流的形式存在,数据一直在产生,没有始末。我们要对数据进行处理时,往往需要明确一个时间窗口,比如,数据在“每秒”“每小时”“每天”的维度下的一些特性。窗口将数据流切分成多个数据块,很多数据分析都是在窗口上进行操作,比如连接、聚合以及其他时间相关的操作。
这里有三种3种常见的窗口形式:滚动窗口、滑动窗口、会话窗口。
- 滚动窗口(Tumbling Window)模式一般定义一个固定的窗口长度,长度是一个时间间隔,比如小时级的窗口或分钟级的窗口。窗口像车轮一样,滚动向前,任意两个窗口之间不会包含同样的数据。
- 滑动窗口(Sliding Window)模式也设有一个固定的窗口长度。假如我们想每分钟开启一个窗口,统计10分钟内的股票价格波动,就使用滑动窗口模式。当窗口的长度大于滑动的间隔,可能会导致两个窗口之间包含同样的事件。其实,滚动窗口模式是滑动窗口模式的一个特例,滚动窗口模式中滑动的间隔正好等于窗口的大小。
- 会话窗口(Session Window)模式的窗口长度不固定,而是通过一个间隔来确定窗口,这个间隔被称为会话间隔(Session Gap)。当两个事件之间的间隔大于会话间隔,则两个事件被划分到不同的窗口中;当事件之间的间隔小于会话间隔,则两个事件被划分到同一窗口。
会话(Session)本身是一个用户交互概念,常常出现在互联网应用上,一般指用户在某App或某网站上短期内产生的一系列行为。比如,用户在手机淘宝上短时间大量的搜索和点击的行为,这系列行为事件组成了一个会话。接着可能因为一些其他因素,用户暂停了与App的交互,过一会用户又使用App,经过一系列搜索、点击、与客服沟通,最终下单。
4、时间
“时间”是平时生活中最常用的概念之一,在流处理中需要额外注意它,因为时间的语义不仅与窗口有关,也与事件乱序、触发计算等各类流处理问题有关。常见的时间语义如下。
- Event Time:事件实际发生的时间。
- Processing Time:事件被流处理引擎处理的时间。
对于一个事件,自其发生起,Event Time就已经确定不会改变。因各类延迟、流处理引擎各个模块先后处理顺序等因素,不同节点、系统内不同模块、同一数据不同次处理都会产生不同的Processing Time。
虽然使用Event Time更准确,但问题在于,因为各种不可控因素,事件上报会有延迟,那么最多要等待多长时间呢?从服务器的角度来看,在事件到达之前,我们也无法确定是否有事件发生了延迟,如何设置等待时间是一个很难的问题。
Watermark是一种折中解决方案,它假设某个时间点上,不会有比这个时间点更晚的上报数据。当流处理引擎接收到一个Watermark后,它会假定之后不会再接收到这个时间窗口的内容,然后会触发对当前时间窗口的计算。比如,一种Watermark策略等待延迟上报的时间非常短,这样能保证低延迟,但是会导致错误率上升。在实际应用中,Watermark设计为多长非常有挑战性。还是以手机游戏为例,系统不知道玩家这次掉线的原因是什么,可能是在穿越隧道,可能是有事退出了该游戏,还有可能是坐飞机进入飞行模式。
那既然Event Time似乎可以解决一切问题,为什么还要使用Processing Time?为了处理延迟上报或事件乱序,需要使用一些机制来等待,这样会导致延迟提高。某些场景可能对准确性要求不高,但是对实时性要求更高,在这些场景下使用Processing Time就更合适一些。