在过去的数年里,我们见证了流处理技术的飞速进步与普及。我第一次接触流处理是在 2012 年。那时候的我有幸在微软亚洲研究院实习,在系统组里做分布式流处理系统。之后我分别在新加坡国立大学、卡耐基梅隆大学、IBM Almaden 研究院、AWS Redshift 从事流处理与数据库系统的研究与开发。如今,我正在 RisingWave Labs(RisingWave: A Cloud-Native Streaming Database)搭建下一代流数据库系统。
一晃 11 年过去,当时在微软亚研院实习的我万万没想到,我在这个领域持续做了十多年,并不断突破边界,从纯技术开发逐步转向探索该领域商业化的道路。在创业公司里,最令人兴奋也最具有挑战的事情便是预测未来 — 根据历史的轨迹思考与判断行业的发展方向。在过去的数月中,我一直在思考几个问题:为什么需要流处理?为什么需要流数据库?流处理系统真的能够颠覆批处理系统吗?在这篇文章中,我将结合自己的软件开发与客户沟通经验,从实践角度探讨流处理与流数据库的过去、现在与未来。
说到流处理系统,大家自然而然的会想到一些低延迟用例:股票交易、异常监控、广告计算等等。然而,在这些用例中,流处理系统到底如何被使用的呢?使用流处理系统时,用户期望的延迟到底有多低?为什么不用一些批处理系统来解决问题?在这里,我来结合自己的经验回答这些问题。
无论什么具体的用例,流处理系统通常被应用在以下两个场景中:数据接入与数据分析。
数据接入:将OLTP数据库与消息队列中的数据做join操作之后插入到数据仓库与数据湖中。
流处理的两个典型场景:数据接入与数据分析。
尽管批处理系统同样能做数据接入与数据分析,但是流处理系统相比于批处理系统,能够将延迟从小时或者天级别降低到秒级或者分钟级。这在一些业务中将带来巨大好处。对于数据接入这个场景中,降低延迟可以让下游系统(比如数据仓库)用户更及时的得到最新的数据,并对最新的数据进行处理。而在数据分析这个场景中,下游系统可以实时看到最新的数据处理结果,从而能够将结果及时呈现给用户。
有朋友一定会问:
这两个问题非常值得深入探讨,我们将会在文章最后一节进行讨论。
对于流处理系统的用户来说,他们期望的延迟到底是多少呢?秒?毫秒?微秒?越低越好?根据我们的客户访谈结果,多数流处理系统用户的用例所需要的延迟在百毫秒到数十分钟不等。在我们的用户访谈中,不少科技企业的在线数据系统工程师对我们说:“使用了流处理系统之后,我们的计算延迟从天级别降到了分钟级,这样的转变已经让我们非常满意了,并没有特别的需求进一步降低延迟。”所谓”延迟越低越好“,在我们看来,听上去很美好,但实际上并没有太多实际用例做支撑。事实上,在一些超低延迟场景中,通用的流处理系统也达不到其所需的延迟需求。
各类真实场景对延迟的需求。
一个很典型的超低延迟场景便是高频量化交易场景。量化公司都期望自己的系统能够在极短时间内响应市场的波动,从而对股票或者期货进行买入卖出。量化公司需要的延迟通常在微秒级别。为了达到这种级别的延迟,许多量化公司都会将自己的机房搬去离交易所物理位置更近的大楼,并精心挑选网络运营商来减少由于网络通信造成的延迟。在这种场景中,量化公司几乎都会选择自己自建系统,而非采用市面上的通用流处理系统(如 Flink、RisingWave 等)。这不仅是因为通用流处理系统往往由于封装达不到延迟的需求,也是因为量化交易通常需要一些特殊的自定义算子,而这些算子一般都不会被通用流处理系统所支持。
还有一些低延迟场景是 IoT(物联网)与网络监控。这类场景的延迟通常在毫秒级,可能是几毫秒,也可能是几百毫秒。在这类场景中,通用流处理系统(如 Flink、RisingWave 等)可能可以做到很好的支撑。但在一些用例中,还是需要使用特化的系统。一个很好的例子就是车载传感器。车载传感器可能会监控车辆的行驶速度、车辆坐标、踩油门与刹车的频率等等信息。这类信息可能由于隐私、网络带宽等原因,一般不会回传给数据中心。因此,常见的解决方案就是在车辆上直接装处理器(或者说是嵌入式设备)来进行数据处理。在这类设备上安装通用流处理系统还是不太合适的。
接下来要谈的便是一些大家耳熟能详的低延迟场景了:广告推荐、欺诈检测、股市大盘报表、订餐 APP 等。这类场景的延迟通常来说都是在百毫秒或者数分钟之间。更低的延迟听起来可能更好,但不一定有必要。就拿股票大盘报表来举例。普通散户通常通过盯着网站或者手机看股票波动来进行交易决策。这些散户真的有需求知道 10 毫秒之前的股票交易信息吗?其实是没有必要的。人眼看到的极限频率是 60Hz,也就是人眼根本分辨不出 16 毫秒以下的刷新。同时,人做决策的反应速度都是在秒级,哪怕是训练有素的运动员听到枪声的反应速度也只能在 100-200 毫秒左右。因此,对于股票大盘这种给人提供决策信息的场景,低于百毫秒的延迟都是没有必要的。而这类需求百毫秒到分钟级的场景,便是通用流处理系统(如 Flink、RisingWave 等)最擅长的场景了。
然后就到了一些对延迟没有很高要求的场景了:酒店预订、库存管理等。对于这类延迟不敏感场景来说,流处理系统与批处理系统其实都能做比较好的支持,因此在用户选择系统的时候,考虑的点往往不是性能,而是成本、灵活性等方面了。
对于机器学习模型训练、数据科学、财务报表等这些对延迟完全没有要求的场景,很显然批处理系统更加适用。当然了,随着技术的不断进步,我们也看到了在线机器学习、在线数据科学等方向的兴起,而不少公司也的确开始使用流处理系统来将这些应用实时化。在本文,我们就不对这类场景进行过多讨论了。
上一节讲了流处理系统的使用场景。在这一节里,我们来谈谈流处理系统的历史。
对于许多资深工程师来说,Apache Storm 也许是他们接触过的第一个流处理的系统。Storm 是使用一门称为 Clojure 的小众 JVM 编程语言编写的分布式流计算引擎。相信很多新一代的程序员可能都没听说过 Clojure 这种语言。Storm 在 2011 年被 Twitter 公司开源。在那个被 Apache Hadoop 统治的年代,Storm 的出现改变了许多人对数据处理的认知。传统来讲,用户处理数据的方式都是将大量数据首先导入到 HDFS,再用 Hadoop 等批计算引擎对数据进行分析。而有了 Storm,用户可以在数据刚流入系统的时候就被处理。用户也能够在几秒钟或者几分钟内获得结果,而不是要等待数小时或者数天。
想要使用Storm做最简单的work count操作,用户也必须学许多Storm特有的概念。图片来源:书籍 “Getting started with Storm”
Storm 在当时是相当有突破性的。然而,早期 Storm 的设计远远没有达到用户预期的完美:它所要求的编程方式过于复杂,并缺少很多现代流处理系统中默认提供的基本功能,例如状态管理、exactly-once 语义、动态扩缩容等等。当然也正是因为这些设计的不完美,才使得诸多才华横溢的工程师去搭建下一代流计算引擎。在 Storm 开源之后两三年内,我们鉴证了一批流计算引擎的诞生:Apache Spark Streaming,Apache Flink,Apache Samza,Apache Heron,Apache S4 等等。 而如今,Spark Streaming 与 Flink 成了非常成功的两个流计算引擎。
如果你认为流处理系统的历史起源于 Storm 的诞生,那就错了。 事实上,流处理系统的历史远比很多人想象的精彩。不算特别意外的是,流处理这一概念来自于数据库圈。在 2002 年的数据库领域顶级会议 VLDB 上,布朗大学与 MIT 的学者发表了“Monitoring Streams – A New Class of Data Management Applications”论文,指出了流处理这一新的需求。在这篇文章中,作者提出,为了处理流数据,我们应该抛弃传统的“Human-Active, DBMS-Passive”传统数据库处理模式,而转向“DBMS-Active, Human-Passive”这一新型处理模式。也就是说,在新型的流处理系统中,数据库应该主动接收数据并触发查询,而人只需要被动接受结果即可。在学术界,最早的流处理系统叫做 Aurora,随后又有 Borealis、STREAM 等系统在同时代诞生。
Aurora: 学术界的第一个流数据处理系统。
在被学术界提出的几年后,流处理技术便在大型数据库厂商中得到了应用。数据库领域的三大老牌厂商 Oracle、IBM、Microsoft 分别推出了他们自己的流处理解决方案,分别称为 Oracle CQL、IBM System S、Microsoft SQL Sever StreamInsight。非常一致的是,三个厂商都选择了在自己现有系统中集成流处理功能,而非将流处理功能单独拿出来开发成独立系统。
流处理系统的变革:从商业化数据库系统的一个功能组件转变成独立的开源大数据系统。
通过以上的讨论,大家可以看到,在 2002 年第一个流处理系统 Aurora 在学术界诞生之后,很快被数据库巨头吸收进自身产品中。但在 2010 年前后,该领域出现了一个重大转变,那便是流处理模块被从数据库系统中独立出来,并单独发展成了完整的、可扩展的分布式流计算引擎。是什么造成了这一变革?我认为是与 Hadoop(或者说 MapReduce)的诞生与发展息息相关。在传统单机数据库世界中,计算与存储都被封装在同一个系统中。尽管这样可以大大简化系统架构,给用户单一接口进行操作,但这种架构无法很好的扩展到集群环境中。Hadoop 所统治的大数据时代的理念便是将计算与持久化存储分割成独立的系统(注意这里所说的“分割成独立的系统”跟人们时常讨论的“存算分离”还是有不少区别),并暴露底层接口给用户,依赖用户提供足够多的信息(例如并行度、partition key 等)来去做水平扩展。这样的模式完美的满足了工程师驱动的新一批互联网公司(如 Twitter、LinkedIn、Uber 等)的需求。我们现在看到的 Storm 及其之后的大数据流计算引擎,无一不是这种设计思路:只负责计算层、暴露底层接口、通过 partition 方式暴力使用资源进行无限扩展。很显然,Storm、Flink 所代表的大数据时代流计算引擎的发展规律,与 Hadoop、Spark 所代表的同一时代的批计算引擎的发展规律完全吻合。
回望历史我们发现,流数据库这一概念在 20 多年前便已被提出并实现。然而,大数据时代所带来的巨大技术变革推动了 Storm、Flink、Spark Streaming 等一批流计算引擎的诞生与发展,并推翻了 Oracle、IBM、Microsoft 这三巨头在流处理技术上的垄断。历史总是螺旋形上升的。在批处理系统领域,我们看到了 Redshift、Snowflake、Clickhouse 等系统重新将人们从“计算引擎”拉回到“数据库“这一理念中来。同样,在流处理领域,我们也看到了如 RisingWave、Materialize 等流数据库的诞生。为什么会这样?我们在这一节详细分析。
随着 2011 年 Storm 开源之后,流计算引擎便进入了发展的快车道。但流数据库并没有就此销声匿迹。有两个知名流数据库系统就诞生在大数据时代中。一个名叫 PipelineDB,另一个名叫 ksqlDB。先不说技术,这两个系统在商业上有着不小的联系。PipelineDB 是于 2013 年创立,2014 年 PipelineDB 团队加入了硅谷知名孵化器 Y Combinator 孵化, 2019 年 PipelineDB 被 Apache Kafka 原创团队所成立的商业化公司 Confluent 收购。而 ksqlDB 正是由 Confluent 公司于 2016 年创立(其实最早是先做了 Kafka Stream)。PipelineDB 与 ksqlDB 尽管都是流数据库,但它们在技术路线上的选择截然不同。PipelineDB 是完全基于 PostgreSQL 的。更准确的说,PipelineDB 使用了 PostgreSQL 的扩展接口。也就是说,只要用户安装了 PostgreSQL,就可以像安装插件一样安装 PipelineDB。这一理念对用户非常友好:用户无需迁移自己的数据库,便可以使用 PipelineDB。PipelineDB 非常核心的卖点就是实时更新的物化视图。当用户将数据插入 PipelineDB 之后,用户所创建的物化视图上便会实时显示最新结果。KsqlDB 选择了与 Kafka 强耦合的策略:在 ksqlDB 中,计算的中间结果存储在 Kafka 中;节点之间的通信使用 Kafka。这种技术路线的优势与缺陷非常鲜明:其优势是高度复用成熟组件,大大降低开发成本,缺陷是强绑 Kafka,严重缺乏灵活性,使得系统的性能与可扩展性大打折扣。
PipelineDB 与 ksqlDB 从用户认可度来讲还是逊色于 Spark Streaming 与 Flink:PipelineDB 已于 2019 年被收购之后停止了更新,而 ksqlDB 由于强绑 Kafka 以及技术成熟度等原因,在 Kafka 生态以外并没有得到足够多的关注。而在最近(2023 年 1 月),Confluent 公司又收购了由 Flink 核心成员创立的 Flink 商业化公司 Immerok,并高调宣布会推出基于 Flink SQL 的计算平台。这使得 ksqlDB 未来在 Confluent 内部的地位变得更加扑朔迷离。
经历了 Hadoop、Spark 领导的大数据时代, 我们便来到了云时代。近年来,诸多云原生系统逐步超越并颠覆了其所对标的大数据系统。一个最为人所知的例子便是 Snowflake 的崛起。Snowflake 基于云构建出的存算分离的新一代云数据仓库形成了对 Impala 等上一代大数据系统的降维打击,在市场上实现了称霸。在流数据库领域,类似的事情可能会再次发生。RisingWave、Materialize、DeltaStream、TimePlus 等就是近几年涌现出来的云上流数据库。尽管商业化路线、技术路线等方面的选择有着各种差异,但它们的大方向都是希望为用户在云上提供流数据库服务。在云上构建流数据库,重点就在于如何使用云的架构来实现无限水平扩展与成本降低。如果能够很好的选择技术路线与产品方向,相信会逐步挑战上一代流处理系统(如 Flink 与 Spark Streaming)的地位。
上面两段简述了流数据库在最近几年的兴起。相信大家都能够看出云原生是个趋势,但为什么大家在云上构建的是“云原生流数据库”,而不是“云原生流计算引擎”?
也许有人会认为是 SQL 的影响。云服务带来的一大变革便是普及数据系统的使用。在大数据时代,系统用户基本都是工程师,他们熟悉 Java 等编程语言进行应用开发。而云服务的兴起急需系统提供一种简单易懂的语言使广大没有编程基础的工作者受益。SQL 这种标准化的数据库语言很显然满足了这个需求。看起来,SQL 的广泛应用推动了“数据库”这个概念在流处理领域的普及。然而,SQL 只是间接因素,而非直接因素。证据很清晰:诸多流计算引擎(如 Flink 与 Spark Streaming)已经提供了 SQL 接口。尽管这些系统的 SQL 接口与 MySQL、PostgreSQL 等传统数据库的 SQL 使用体验有极大区别,但至少证明了有 SQL 不一定代表要有数据库。
我们回看“流数据库”与“流计算引擎”的区别,会发现流数据库拥有自的存储,而流计算引擎并没有。在这一表象底下更加深层的理念是:流数据库将存储视为一等公民(first-class citizen)。这一理念使得流数据库很好的满足了用户的两个最根本需求:简单、好用。这是如何做到呢?我们列举以下几个方面。
流数据库这一系统可以替代流计算引擎+服务系统这一系统组合。
在流计算引擎中, 跨系统外部调用会造成巨大性能代价。
(这一节的讨论可能会显得无趣,因为已经有太多文章讨论过云原生系统的设计与实现了。大家可以选择跳过。)
云与集群的最大区别在于,云可以被认为是资源无限,且资源解耦;而集群是资源有限,且资源耦合。什么意思呢?第一,云上用户已经不再需要感知物理机器的数量:他们只需要付钱就可以获得他们想要的资源;而大数据时代的集群用户往往只拥有有限的物理机器;第二,云对用户暴露出来的是分类资源:用户可以根据需求单独购买计算、存储、缓存等资源。而大数据集群暴露出来的就是一台一台物理机器,用户只能是按机器数量来请求资源。第一点区别使得数据系统的设计目标发生了本质转变:大数据系统的目标是在有限资源内最大化系统性能,而云系统的目标是在无限资源内最小化成本开销;第二点区别则使得数据系统的实现方式发生了本质转变:大数据系统通过存算耦合的 shared-nothing 架构实现暴力并行,而云系统则通过存算分离的 shared-storage 架构实现按需伸缩。在流处理系统中,所谓中间计算结果即是存储。当中间计算结果需要从计算节点剥离出来放到 S3 等持久化云存储服务上时,大家会很自然的想到,S3 带来的数据访问延迟可能大幅影响流处理系统这种低延迟系统的性能。因此,云原生流处理系统不得不去考虑使用计算节点的本地存储以及外挂存储(如 EBS 等)去缓存数据,从而最大化减小 S3 访问带来的性能下降问题。
大数据系统与云系统的优化目标不同。
流处理技术因其能够极大降低数据处理延迟,被很多人视为一种可以颠覆批处理的技术。当然也有另一种观点认为,大多数批处理系统都已经“升级”成实时分析系统,流处理系统的价值将非常有限。我自己投身于流处理技术的研发与商业化,自然对流处理的前景极度乐观。而我并不认同流处理与批处理会互相取代。在本章,我们详细探究流处理与批处理各自的独特之处。
目前多数的批处理系统,包括 Snowflake、Redshift、Clickhouse 等,都宣称自己是实时分析系统,能够对数据进行实时大规模分析。我们在第一章也提到一个问题:对于数据分析场景,在已有许多实时分析系统的情况下,为什么还要用流处理系统?”我认为这完全是对所谓”实时“定义的不同。在流处理系统中,查询被用户事先定义,而查询处理由数据驱动;在实时分析系统中,查询由用户随时定义,而查询处理由用户驱动。流处理系统所说的“实时”是指系统对用户预定义的查询实时给出最新的结果,而实时分析系统所说的”实时“是指用户随时给出的查询实时给出结果。没看出来区别?那更简化一下,就是流处理系统强调计算结果的实时性,而实时分析系统强调用户交互的实时性。对于股票交易、广告计算、网络异常监控等对数据结果新鲜度要求很高的场景,流处理系统也许是最佳选择;而对于交互式自定义报表、用户行为日志查询等对用户交互式体验要求很高的场景,实时分析系统可能会更胜一筹。
流数据库与实时分析数据库的区别。流数据库先计算后存储,计算由数据驱动,注重结果的实时性;实时分析数据库先存储后计算,计算由用户驱动,注重用户交互的实时性。
也许有人会说,既然实时分析系统能够对用户发送的查询实时给出结果,那么只要用户一直向实时分析系统中发送相同的查询,岂不是就能时刻保证结果的新鲜度,实现流处理系统的效果?这种想法有两个问题。第一个问题是实现复杂。用户毕竟不是机器,无法一直守在电脑前不间断的发送查询。想要实现这一效果无非只有两条路:要么是自己写程序定时发送查询,要么是自己运维编排工具(如 Airflow 等)循环发送查询。无论是哪条路,都意味着用户需要付出额外的代价运维外部系统(或程序)。第二个问题(也是最根本的问题)是成本过高。原因很简单:实时分析系统进行的是全量计算,而流处理系统进行的是增量计算。当所需处理的数据量较大时,实时分析系统不得不进行大量冗余的计算,带来巨量资源浪费。说到这里,相信大家应该也对之前本文第一章“为什么不拿个编排工具定时触发批处理系统做计算”这个问题有了答案。
流处理系统采用增量计算方式避免不必要的重复计算。
如今诸多实时分析系统都已经提供了实时物化视图功能,而实时物化视图就是流处理在数据库内的表达形式。有种观点认为,有了带有实时物化视图的分析系统,我们就不再需要需要单独的流处理系统。我认为这个观点并不成立。我们可以从以下几个方面考虑。
流处理并不是万能的,流处理也不无法彻底替代批处理。有几方面的原因。
讨论了这么多,相信大家也看出来,流处理与批处理各具特点,很难在全场景中实现完全替代。既然这两种处理模式会共存,那很自然有些人会想到在同一套系统中同时支持流处理与批处理。不少系统已经进行了一些探索,这里就包括了 Flink、Spark 等这类老牌大数据系统。尽管这些系统的流批一体方案已经在一些场景落地,但是从实际的市场接受度来看,至少目前来讲,大多数用户仍然选择分别部署流处理、批处理两套系统。这其中不仅包含性能、稳定性的考量,同时也能在功能上各取所长:既保证了实时性,又同时能对归档数据运行复杂的 AI 算法、ad-hoc 分析等等。
目前阶段, RisingWave 还是更加专注于流处理本身,但也会通过 Sink、开放格式以及第三方 connector 等方式,方便用户使用第三方实时分析系统进行数据分析。事实上,在现在的 RisingWave 版本中,用户可以很轻松的将数据导出到 Snowflake、Redshift、Spark、ClickHouse 等系统中。我认可流批一体方案的意义,从长期来讲,RisingWave 也会进行这方面的探索。实际上,流数据库就是在做流处理与批处理的统一:当有新的数据流入流数据库时,流数据库就进行流计算;当有用户发起随机查询请求时,流数据库就进行批计算。至于内部实现如何,本文就不再展开赘述了,我们可以开一篇新文章详细探讨流批一体的设计。
在文章最开始的时候,我提到自己已经在流处理领域做了 11 年的时间。然而,在 2015 到 2016 年的时候,我一度认为流处理的这座大厦已经建成,剩下的工作仅是小修小补。那时的我并没有想到,云计算的快速发展与普及让流处理系统在 2020 年之后重新回到了舞台的正中央,越来越多的人正在研究、开发、使用这一技术。本文是我最近几个月结合技术与商业化对流处理进行的思考。希望能够抛砖引玉,欢迎大家一起讨论!
感谢 Tianshuo Shi,Eric Fu,Tianyi Zhuang 对本文提出的意见与建议!
领取专属 10元无门槛券
私享最新 技术干货