Stream is the new file

Stream is the new file
作者:
随着5G 网络、容器云、高性能存储硬件水平的不断提高,流处理正在拥有越来越广泛的市场前景。各种各样的设备传感器、监控摄像头、移动终端设备等等无时无刻不在产生着大量的流式数据。针对不同的场景,流式数据也许会有各式各样不同的特点,但是对于这些流式数据的处理,往往都有实时或者接近于实时的、无边界连续不断的、低延时的共性要求。而这些要求,恰恰就是流处理的基本特点。
#技术探索

Stream is the new file

滕昱

滕昱Pravega 中国社区创始人,戴尔科技集团软件开发总监,DellEMC OSA(Object, Stream and Analytics)

随着5G 网络、容器云、高性能存储硬件水平的不断提高,流处理正在拥有越来越广泛的市场前景。各种各样的设备传感器、监控摄像头、移动终端设备等等无时无刻不在产生着大量的流式数据。针对不同的场景,流式数据也许会有各式各样不同的特点,但是对于这些流式数据的处理,往往都有实时或者接近于实时的、无边界连续不断的、低延时的共性要求。而这些要求,恰恰就是流处理的基本特点。

随着5G 网络、容器云、高性能存储硬件水平的不断提高,流处理正在拥有越来越广泛的市场前景。

各种各样的设备传感器、监控摄像头、移动终端设备等等无时无刻不在产生着大量的流式数据。针对不同的场景,流式数据也许会有各式各样不同的特点,但是对于这些流式数据的处理,往往都有实时或者接近于实时的、无边界连续不断的、低延时的共性要求。而这些要求,恰恰就是流处理的基本特点。

null

仅仅从这些基本特点看,好像流处理早已经被实现了。不管是70年代开始兴起的规则引擎,还是基于传统的关系型数据库的复杂海量数据处理,貌似都符合要求,甚至在编程语言里都既有了对这些处理系统的支持。但是Stonebraker,Çetintemel和Zdonik在2005年的论文《The 8 Requirements of Real-Time Stream Processing》中指出,现有的这些系统其实还不能真正满足流处理的要求,流处理技术还需要进一步的发展。

null

这也是为什么现阶段包含Flink在内的流批一体的大数据技术栈持续在不停发展的原因。IDC报告指出,未来的3~5年整个实时数据会以惊人的速度增长。那么不管是针对实时数据的流处理,还是针对历史数据的批处理,也都需要同步的发展来满足时代的要求。

null

这些流批一体的大数据技术栈的发展,不光包含Flink等处理引擎的进化,也包括存储领域。那么在存储领域,针对现代流处理,又有哪些进化和发展呢?

null

传统来说,数据往往以文件的形式组织,用文件系统加以管理。

null

但是,文件接口抽象真的能很好的处理流式的连续数据么?让我们来看一看。

null

首先,假设我们用文件和文件系统来做一个流存储。把传感器、用户日志、用户输入这些数据注入一个文件中,貌似并没有什么问题。但是被写入文件的数据必须被持续不断的读取出来。也就是说,持续不断被写入文件的数据,必须不停的被读取出来用以处理,这是文件接口和针对连续数据流处理最根本的区别。

null

其次,当数据量变大的时候,并发是必须的。我们当然可以利用多个文件实现并发写。但这也意味着读端的应用程序必须追踪多文件读。为增加并发而带来的多文件读取的协调和追踪并没有包含在文件接口的抽象里,所以这对读应用程序来说,并不是透明的。

null

第三,如果进一步考虑动态扩展呢?动态扩展意味着在程序读取的过程中再动态生成新的文件或者合并已有文件以适应新的并发度。在这种情况下,读端应用程序需要自己监测在读文件的新增和减少,这是除应用程序本身业务逻辑之外额外的工作。

null

第四,数据是连续无边界的,需要一种标记来记录当前数据的读取位置。横跨多个文件去设计逻辑上全局一致的位置点,进一步增加了应用程序的复杂性。

null

第五,IOT场景往往需要维护针对同一设备号的数据序列。如果把设备数据当作文件,把设备号当作key,那么注入端需要key到文件的映射处理并维护在同一key命名空间下的per-key order。同时,读取端还得做到多文件读取的负载均衡。这些都是文件和文件系统抽象不能完成,所有的工作都推向了上层应用程序。

null

第六,对于流处理来说,数据的清除往往是从流数据的头开始删除,先写入的先删。文件接口抽象并不能很好的处理这点。

null

近些年来,业界其实是广泛应用了一个中间解决方案,通过messaging系统(比如Kafka)+文件系统的混合抽象方案注入。这解决了部分问题,比如说动态扩展、注入端的并行问题。但是这不是一个完整的端到端解决方案。实时流计算是走了messaging接口规避了文件接口的一些问题,但是针对历史数据的批处理还是需要文件接口,这实际上是针对同一数据的两种系统。

null

所以,对于连续的流式数据的存储层抽象,我们需要的既不是原来的基于传统数据库的实现,也不是基于messaging系统的转化,而是从头设计一个完整的流存储系统。

null

那么,这种流存储的抽象能给上层的计算单元带来什么样的好处呢?让我们来具体看一下。

null

首先,对于之前提到的messaging系统+文件系统,数据需要用stream接口进入messaging系统,但是可能以文件接口方式读出,在接口抽象上并不一致。我们需要的流存储抽象,不管是注入端还是读取端,都是stream接口,给应用程序统一的抽象。

null

其次,流存储抽象需要提供动态扩展功能。在应用程序看来,它只需要往一个stream里写入数据。至于这个stream抽象怎么基于注入量进行动态扩展,或者在多路并发下怎么保证per-key的order,由抽象层内部解决,对应用程序完全透明。

null

第三,在所有情况下,哪怕是动态扩展过程中,从流存储抽象层读出的数据,具有per-key的order保证。

null

第四,流存储抽象能够在逻辑上提供基于时间的全局一致的位置点,我们称之为Stream Cut。应用程序依赖于此能够回放到任意一个位置点,回放或重试业务逻辑。

null

计算引擎例如Flink能够利用流存储抽象提供的Stream Cut,基于流存储系统处理的checkpointing功能,实现端到端的exactly-once保证。这在文件抽象接口上,是很难做到的。

null

除此之外,还有很多其他针对streaming典型场景的的好处,例如原子读写,低延时的tail read、事务支持、历史数据truncation等等。

null

那么,假设有了这个很好的流存储抽象出现,它能做什么?

我们能够基于这层抽象,建造更简单、更清楚的大数据的流水线。

null

海量的连续流式数据注入这个流水线,被保存到流存储中。以Flink为代表的流批一体的处理单元用流存储提供的统一接口,包括针对流处理的低延迟的tail read,以及针对批处理的高吞吐的historical read,针对同一份数据,提供支持exactly-once语义的数据处理。一种抽象一套处理,简化流程。

null

当然实际中流水线会更加复杂一些。数据往往是被写入Edge端,进行on-the-fly的实时计算处理,比如监控摄像头拍下的图片图像的预处理。同时,数据也可以被发送到数据中心的私有云或者是公有云上,作更大规模的准实时的一个计算。这样的方式,让大数据流水线的开发变得非常的清楚和简洁。

null

Pravega(梵语:high speed)就是在流存储抽象的需求背景下应运而生的系统,具有前面提到的流存储抽象的所有特点。Pravega是2016年创建开源项目Apache2 License,近期已被加入CNCF。

null

下面我们就来看看,Pravega是怎么提供以上提到的流存储抽象的这些属性的。

首先,对于近期写入的数据,Pravega提供低延迟的tail read。同时,Pravega底层由可扩展的软件定义存储实现,可以支持无限历史数据存储。并且这些历史数据同样通过streaming接口读取,以实现针对历史数据的流处理。其次,Pravega支持动态扩展。根据前端流量的大小,Pravega能够动态调整partition的数量以适应前端流量,对客户端透明。再次,Pravega提供StreamCut方便客户获取基于时间的数据分片,用来实现数据会放处理功能等。当然,Pravega还支持以streaming接口truncate数据,从头读取历史数据等等。基本上之前提到的相关特点在Pravega里都能找到相应的功能支持。

null

那Pravega具体是怎么实现的呢?

当创建一个stream的时候,和其他可扩展系统不同,用户并不需要指定并发的个数。在Pravega内部,segment是真正的数据存储单元。一个stream可以拥有一个或多个segment(s)。Pravega通过动态调整segment的个数实现动态扩展。

所有写入stream的数据都被当作是一串append only的bytes最终写入segment中。可以是一行log,可以是一张图片,通过serializer/deserializer决定语义,没有格式的限制,也没有必须是小文件的限制。

null

当stream拥有多个segment的时候,数据会并发写入这多个segment中。这多个segment将namespace分成同等数量的key space,写入的数据可以通过绑定routing key,决定自己写入哪个key space(segment)中。相同routing key的数据会被写入同一个segment中,获得order保证。比如,传感器产生的连续数据都可以使用传感器的设备号作为routing key,以保证同一传感器产生的数据拥有相同的routing key而被写入同一个segment,以保证读取时的时序性。实际上,Pravega的transaction,exactly-once等特性正是基于此实现的。

null

讲了那么多动态扩展,下面给大家一个具体的例子看看它的实现。假设系统中创建了一个stream,开始的时候他只有两个segment。

null

当注入流量翻倍的时候,Pravega能够检测到这点,并且将segment的个数从2个扩展成4个。这点不需要用户的任何干预,不需要改变配置、扩展节点、起停服务等等,所有的都无缝发生在Pravega内部,对用户透明。

null

同样,当注入流量减少时,Pravega也能相应的合并segment,去除不必要的并发节省资源使用。

null

Pravega的这种动态扩展机制,结合container化的部署方式,让Pravega真正实现了cloud-native的分布式可扩展的流存储系统。

下面是Pravega的架构图。左边是一个非常抽象的stream,用户通过Event Stream Writer/Reader通过streaming接口读写数据。右边可以分成两部分,控制面板和数据面板。控制面板负责管理和维护stream和segment,比如stream的创建, segment的分配部署,以及segment的动态扩展等。数据面板以segment为单位管理数据。写入segment的数据首先会被写入Durable Log实现数据的持久化保护。同时数据也会缓存在Streaming Cache中,提供高性能的读取。所有写入的数据在积攒后会通过优化算法打包写入底层可扩展的Long-term Storage,通过分级存储保存历史数据。这层Storage只做数据存储功能,对于历史数据的读取依然通过Pravega的streaming接口提供。数据面板除了通过segment来管理用户数据外,也通过Table segment管理自己的metadata数据。它同样支持动态扩展,避免了很多系统用zookeeper存放metadata是遇到的扩展问题。

null

好,到此为止,我们应该了解到Pravega确实是符合流存储抽象的实现。那么随后的一个问题是,支持了这么多灵活的功能,实现应该很复杂吧。这样的一个流存储系统,运行起来到底性能会怎么样呢?毕竟对于实时性要求比较高的流处理来说,性能是至关重要的。

null

为了验证这点,我们把Pravega 0.8部署在AWS标准服务商,用业界标准的OpenMessaging Benchmark系统,对Pravega的性能进行了测试和取样。完整的结果在《When speeding makes sense — Fast, consistent, durable and scalable streaming data with Pravega》(https://blog.pravega.io/2020/10/01/when-speeding-makes-sense-fast-consistent-durable-and-scalable-streaming-data-with-pravega/)这篇博客上可以找到。

这里我们截取了其中的一些对Pravega的性能进行一些介绍。

下面这张图显示了Pravega在1个segment和16个segment下,随着注入量的不断增加,Pravega的性能表现。我们可以看到,Pravega性能针对不同segment并没有太大区别,都能够做到低延时高吞吐。随着注入量的增大,性能成稳定线性变化。足以说明Pravega在性能方面的亮眼稳定表现。

null

此外,我们还和messaging系统(Pulsar)对比了分级存储的性能。测试中,对于同样部署在AWS上Pravega和Pulsar,我们用OpenMessaging对两套系统用相同的注入速度持续写入15分钟,以使两套系统上有大约100GB的历史数据。然后同时打开读端读取数据,考验两套系统对于历史数据读取的表现。从图上我们可以看到,Pravega在短短几分钟内就能够读取并消化掉之前的历史数据,追赶上前端新的写入。而Pulsar花费80分钟依然没有做到。这也正是Pravega作为一个流存储系统而不是messaging系统必须具备的优势,对历史数据的存储和读写同样重要。

null

对于Pravega引以为傲的动态扩展机制,我们也给出了相关测试。在下面的图示中,测试stream刚开始只有1个segment。在高注入量的持续注入下,图示可以看到stream的segment每隔大约10分钟自动扩展一次,随着每次扩展,系统延迟降低一次。整个过程完全自动,最终系统会针对注入的数据量,达到最佳性能平衡。完美的设计!

null

那是不是segment越多越好呢?我们都有类似的经验,segment越多,资源竞争越激烈,系统会出现超负载的情况,性能反而会更糟。那Pravega是这样的情况么?

我们也做了和Kafka的对比图。当segment个数从1涨到10的时候,确实,对于两套系统来说,segment个数越多,吞吐率越高。但是显然,10是峰值,超过以后Kafka如经验预料的一样,性能开始有了显著下降。但是Pravega依旧能够维持峰值的高性能不变。足以说明Pravega的性能在扩展时的稳定性。

null

由上所有的架构介绍和性能分析,我们可以看到,Pravega确实是一个合格的企业级的cloud-native分布式可扩展流式存储系统。

有了这样一个系统,建造企业级的流处理系统变得相对简单。我们就基于Pravega建造了一个可扩展的流批一体的流式搜索系统:Pravega Search。

null

可以把Pravega Search看作是类似于Elasticsearch或者Splunk产品类似的搜索系统。它同样可以针对注入数据创建索引,通过索引查询提供搜索结果。但是,Pravega Search考虑流处理的特点,支持针对流数据的continuous query。在连续数据的不断注入时,同时给出实时的计算结果。这是Elasticsearch所没有的。

null

这就是基于流存储系统Pravega构建流处理应用的便捷和优势。在批流一体的流水线上,Pravega stream作为数据管道,把上层一个个的计算单元耦合起来。比如图中所示,用户数据流入Pravega stream后,流入continuous query进行计算,计算结果数据又重新流回Pravega stream不断套接。同时,不管是针对流处理的continuous query还是基于历史数据的传统批处理,数据只存储了一份,避免了现在批流一体的大数据处理流水线上数据在多个不同集群之间重复复置存储的问题。

null

综上所述,随着流处理的不断发展,流存储系统也从早期的基于传统数据库,到现在的新型架构体系不断发展,并且依然拥有广阔的发展前景。

在未来流存储系统的发展蓝图里,message系统已经不能完全满足技术发展对于流存储系统的所有幻想。Pravega应流存储系统需求而生,提供纯粹的流存储抽象,旨在促进批流一体的大数据流处理系统的发展。

作为CNCF的大数据流处理生态中的一员,Pravega和其他开源流处理系统例如Flink,必将给大数据流处理领域发展带来新的色彩,让我们拭目以待!

null


欢迎转载,敬请在正文中标注并保留文章来源、原文链接、作者/译者等信息。

Flink SQL 在网易云音乐的产品化实践
  • 关注
    • qr_code

      微信公众号

      最新前沿最热资讯

    • qr_code

      技术支持钉钉群

      时时刻刻得到帮助

  • TOP