2023- 吴英俊- 重新思考流处理与流数据库
重新思考流处理与流数据库

在过去的数年里,我们见证了流处理技术的飞速进步与普及。我第一次接触流处理是在
一晃
流处理系统的正确使用姿势
说到流处理系统,大家自然而然的会想到一些低延迟用例:股票交易、异常监控、广告计算等等。然而,在这些用例中,流处理系统到底如何被使用的呢?使用流处理系统时,用户期望的延迟到底有多低?为什么不用一些批处理系统来解决问题?在这里,我来结合自己的经验回答这些问题。
典型流处理场景
无论什么具体的用例,流处理系统通常被应用在以下两个场景中:数据接入与数据分析。

数据接入:将
** 数据接入(data ingestion) 。** 所谓数据接入,就是将数据从一个(或多个)数据系统经过一定计算之后插入到另一个(或多个)数据系统中。另一种常见的说法便是ETL 。用户为什么要做数据接入?我举几个简单例子大家就明白了。我们可以考虑有个电商网站,使用一个OLTP 数据库(比如AWS Aurora 、CockroachDB、TiDB 等)支撑线上交易。同时,为了更好的分析用户的行为,网站也可能使用了一个程序采集用户行为(比如点击广告等) ,并将用户行为日志插入了消息队列(如Kafka 、Pulsar、Redpanda 等)中。为了更好的提升销量,电商网站通常会将交易数据与用户行为日志导入到同一个数据系统,如Snowflake 或是Redshift 这样的数据仓库中,以便进行分析。在这里,电商网站的工程师们便可以使用流处理系统将数据从OLTP 数据库与消息队列实时的搬到数据仓库里。在数据搬运的过程中,流处理系统会做各类计算,来进行脏数据清理、多个数据源join 等操作。在我们接触过的场景中, 数据的源头往往是OLTP 数据库、消息队列、存储系统等,而数据的终点除了OLTP 数据库、消息队列、存储系统外,更常见的便是数据仓库与数据湖。值得一提的是,我们目前没有见过从数据仓库与数据湖导出数据到其他系统的案例,主要原因还是用户通常将数据仓库与数据湖看作是数据的终点,同时由于数据仓库与数据湖一般不提供数据变更日志,这使得数据实时导出更加困难。** 数据分析(data analytics) 。** 数据分析很容易理解,就是对一个(或多个)数据系统中的数据进行计算得到分析结果,并将结果推送给用户。当使用流处理系统做数据分析时,往往意味着用户希望是对最近(比如30 分钟、1 小时、7 天等)的数据更加感兴趣,而不是去到数仓中批量处理数月甚至数年的数据。在数据分析场景中,流数据系统的上游往往还是OLTP 数据库、消息队列、存储系统等,而下游通常是个key-value store (如Cassandra 、DynamoDB 等)或者是个缓存系统(如Redis 等) 。当然,也有些用户会直接将流处理的结果发送给应用层,这种使用方式一般在告警系统中比较普遍。

流处理的两个典型场景:数据接入与数据分析。
尽管批处理系统同样能做数据接入与数据分析,但是流处理系统相比于批处理系统,能够将延迟从小时或者天级别降低到秒级或者分钟级。这在一些业务中将带来巨大好处。对于数据接入这个场景中,降低延迟可以让下游系统(比如数据仓库)用户更及时的得到最新的数据,并对最新的数据进行处理。而在数据分析这个场景中,下游系统可以实时看到最新的数据处理结果,从而能够将结果及时呈现给用户。
有朋友一定会问:
- 对于数据接入(或者
ETL )这个场景,我们拿个编排工具(比如Apache Airflow )定时触发批处理系统(比如Apache Spark )做计算不就可以了吗? - 对于数据分析场景,许多实时分析系统(比如
Clickhouse 、Apache Pinot、Apache Doris 等)都能对数据进行在线分析,为什么还要用流处理系统?
这两个问题非常值得深入探讨,我们将会在文章最后一节进行讨论。
用户的期望:延迟到底需要有多低?
对于流处理系统的用户来说,他们期望的延迟到底是多少呢?秒?毫秒?微秒?越低越好?根据我们的客户访谈结果,多数流处理系统用户的用例所需要的延迟在百毫秒到数十分钟不等。在我们的用户访谈中,不少科技企业的在线数据系统工程师对我们说

各类真实场景对延迟的需求。
一个很典型的超低延迟场景便是高频量化交易场景。量化公司都期望自己的系统能够在极短时间内响应市场的波动,从而对股票或者期货进行买入卖出。量化公司需要的延迟通常在微秒级别。为了达到这种级别的延迟,许多量化公司都会将自己的机房搬去离交易所物理位置更近的大楼,并精心挑选网络运营商来减少由于网络通信造成的延迟。在这种场景中,量化公司几乎都会选择自己自建系统,而非采用市面上的通用流处理系统(如
还有一些低延迟场景是
接下来要谈的便是一些大家耳熟能详的低延迟场景了:广告推荐、欺诈检测、股市大盘报表、订餐
然后就到了一些对延迟没有很高要求的场景了:酒店预订、库存管理等。对于这类延迟不敏感场景来说,流处理系统与批处理系统其实都能做比较好的支持,因此在用户选择系统的时候,考虑的点往往不是性能,而是成本、灵活性等方面了。
对于机器学习模型训练、数据科学、财务报表等这些对延迟完全没有要求的场景,很显然批处理系统更加适用。当然了,随着技术的不断进步,我们也看到了在线机器学习、在线数据科学等方向的兴起,而不少公司也的确开始使用流处理系统来将这些应用实时化。在本文,我们就不对这类场景进行过多讨论了。
回顾(被遗忘的)历史
上一节讲了流处理系统的使用场景。在这一节里,我们来谈谈流处理系统的历史。
Apache Storm 与其之后的系统
对于许多资深工程师来说,

想要使用
Apache Storm 之前的历史
如果你认为流处理系统的历史起源于

在被学术界提出的几年后,流处理技术便在大型数据库厂商中得到了应用。数据库领域的三大老牌厂商
2002 年至今:到底什么被改变了?

流处理系统的变革:从商业化数据库系统的一个功能组件转变成独立的开源大数据系统。
通过以上的讨论,大家可以看到,在
流数据库的复兴
回望历史我们发现,流数据库这一概念在
PipelineDB 与ksqlDB 的故事
随着
云原生流数据库的兴起
经历了
流计算引擎与流数据库
上面两段简述了流数据库在最近几年的兴起。相信大家都能够看出云原生是个趋势,但为什么大家在云上构建的是“云原生流数据库”,而不是“云原生流计算引擎”?
也许有人会认为是
我们回看“流数据库”与“流计算引擎”的区别,会发现流数据库拥有自的存储,而流计算引擎并没有。在这一表象底下更加深层的理念是:流数据库将存储视为一等公民(first-class citizen
** 统一数据操作对象。** 在流计算引擎中,流(stream)是基本的数据操作对象;在数据库中,表(table)是基本的数据操作对象。在流数据库中,流与表的概念得到了完美的统一:用户不再需要考虑流与表的区别,而是可以把流看做是无限大小的表,并使用操作传统数据库的方式对表这个概念进行处理。- 简化用户数据栈。相比于流计算引擎,流数据库拥有了对数据的所有权:用户可以直接将数据存储在表中。当用户通过流数据库处理完数据之后,可以直接将处理后的结果存储在同一系统中,供用户进行并发访问。而由于流计算引擎无法存储数据,这就意味着其进行计算之后,必须将结果导出到
key-value store 或缓存系统中,才能供用户访问。这也就是说,用户可以使用单一的流数据库系统替换掉之前Flink+Cassandra/DynamoDB 等服务组合。简化用户的数据栈,削减运维成本,这很显然是很多公司期待的。

流数据库这一系统可以替代流计算引擎
** 减少外部访问开销。** 现代企业的数据源是多样的。当使用流处理系统的时候,用户往往需要访问外部系统数据(考虑要将Kafka 导出的数据流与MySQL 中的一个表做join ) 。对于流计算引擎来说,要想访问MySQL 中的数据,必须进行一次跨系统外部调用。这种调用造成的性能代价是巨大的。当流数据库拥有存储数据的能力之后,很显然能将外部系统中的数据保存(或缓存)在流数据库内部,从而大幅提升数据访问性能。

在流计算引擎中, 跨系统外部调用会造成巨大性能代价。
-
提供结果可解释性与一致性保证。流计算引擎的一大痛点在于计算结果缺乏可解释性与一致性保证。我们考虑一个非常简单的例子:用户使用
Flink 提交了两个job ,一个是求过去十分钟内Tesla 股票的被买入次数,另一个是求过去十分钟内Tesla 股票的被卖出次数。在Flink 中,不同job 独立运行,两个job 不断向下游系统输出结果。由于流计算引擎的计算进度不同、输入输出不被系统管理,导致下游系统接收到的两个结果缺乏一致性(比如一个可能是8 点10 分的结果,另一个可能是8 点11 分的结果) ,也无法被溯源。看到这样的结果,用户是非常困惑的:他们无法判断结果是否正确、如何得出、如何演变。而当流数据库可以拥有对输入、输出数据的所有权之后,系统的计算行为从理论来说都变得可观测、可解释、强一致了。毕竟,在流数据库中,一切计算的输入数据都可以被存储到表中并打上时间戳,一切计算产生的结果都可以保存在物化视图中并通过时间戳溯源。这样,用户就可以很好的理解计算结果了。当然理论归理论,实际还得看系统是否实现。RisingWave 就是实现了这种强一致性并提供可解释性的系统之一。 -
** 深度优化计算执行。** 将“存储被视为一等公民”,意味着流数据库的计算层可以感知存储,而这种感知能力使得系统能够在查询优化层以及计算执行层进行大幅优化。一个简单的例子就是可以更好的共享计算状态节省资源开销。由于涉及大量技术细节,我们不在这里进行过多讨论,有兴趣的朋友可以参考其他一些文章:https://zhuanlan.zhihu.com/p/521759464。
云原生流数据库的设计准则
(这一节的讨论可能会显得无趣,因为已经有太多文章讨论过云原生系统的设计与实现了。大家可以选择跳过
云与集群的最大区别在于,云可以被认为是资源无限,且资源解耦;而集群是资源有限,且资源耦合。什么意思呢?第一,云上用户已经不再需要感知物理机器的数量:他们只需要付钱就可以获得他们想要的资源;而大数据时代的集群用户往往只拥有有限的物理机器;第二,云对用户暴露出来的是分类资源:用户可以根据需求单独购买计算、存储、缓存等资源。而大数据集群暴露出来的就是一台一台物理机器,用户只能是按机器数量来请求资源。第一点区别使得数据系统的设计目标发生了本质转变:大数据系统的目标是在有限资源内最大化系统性能,而云系统的目标是在无限资源内最小化成本开销;第二点区别则使得数据系统的实现方式发生了本质转变:大数据系统通过存算耦合的

大数据系统与云系统的优化目标不同。
流处理还是批处理:替代还是共存?
流处理技术因其能够极大降低数据处理延迟,被很多人视为一种可以颠覆批处理的技术。当然也有另一种观点认为,大多数批处理系统都已经“升级”成实时分析系统,流处理系统的价值将非常有限。我自己投身于流处理技术的研发与商业化,自然对流处理的前景极度乐观。而我并不认同流处理与批处理会互相取代。在本章,我们详细探究流处理与批处理各自的独特之处。
流处理与实时分析
目前多数的批处理系统,包括

流数据库与实时分析数据库的区别。流数据库先计算后存储,计算由数据驱动,注重结果的实时性;实时分析数据库先存储后计算,计算由用户驱动,注重用户交互的实时性。
也许有人会说,既然实时分析系统能够对用户发送的查询实时给出结果,那么只要用户一直向实时分析系统中发送相同的查询,岂不是就能时刻保证结果的新鲜度,实现流处理系统的效果?这种想法有两个问题。第一个问题是实现复杂。用户毕竟不是机器,无法一直守在电脑前不间断的发送查询。想要实现这一效果无非只有两条路:要么是自己写程序定时发送查询,要么是自己运维编排工具(如

流处理系统采用增量计算方式避免不必要的重复计算。
流处理与实时物化视图
如今诸多实时分析系统都已经提供了实时物化视图功能,而实时物化视图就是流处理在数据库内的表达形式。有种观点认为,有了带有实时物化视图的分析系统,我们就不再需要需要单独的流处理系统。我认为这个观点并不成立。我们可以从以下几个方面考虑。
** 资源竞争。** 分析型数据库要解决的核心问题是在大规模数据集上高效的对复杂查询进行处理。在这类系统中, 物化视图的定位本质上与数据库索引无异:都是计算的缓存。创建这样的缓存有两个好处:一方面,为经常处理的查询创建物化视图可以有效避免重复计算;另一方面,为不同查询的共享子查询创建物化视图可以加速查询执行。这样的定位实质上使得物化视图几乎不可能得到及时更新:积极主动的更新物化视图势必会持续抢占计算与内存资源,导致用户发送的查询得不到及时响应。为了防止这种“本末倒置”的事情发生,几乎所有分析性数据库采用的都是被动更新(也就是需要用户主动驱动)或是延迟更新(等到系统空闲时再更新)的方式。** 正确性保证。** 如果说资源竞争问题可以通过多加计算节点来解决,那么正确性问题就不是实时分析系统的物化视图能够解决的问题了。批处理系统处理的是有限有边界数据,而流处理系统处理的是无限无边界数据。在处理无边界数据时,由于网络通信等各种原因可能产生数据乱序问题。而流处理系统特别设计了水位线等概念来解决这一问题。当乱序数据只有当按照某一特定顺序至行之后,输出的结果才被认为是完全正确的。然而,实时分析系统缺少水位线等基础设计,这使得无法达到流处理系统所能达到的正确性。而这种正确性保证在各种流处理场景(比如风险控制、广告计算等)中至关重要。缺少了正确性保证的系统,自然无法替代流处理系统。
流处理的软肋
流处理并不是万能的,流处理也不无法彻底替代批处理。有几方面的原因。
** 灵活性。** 流处理要求用户事先预定义好查询,从而来实现不间断的对最新数据进行实时计算。这一要求使得流处理在灵活性方面弱于批处理。正如本文之前所提到的,尽管流处理对查询相对固定的场景有很好的支持,但是当面对需要与用户频繁交互的场景时,批处理系统会更加适合。** 表达性。** 我们在上文提到,流处理使用增量计算的方式通过避免冗余计算来减小资源开销。但增量计算也带来了一大问题,就是系统的表达性受限。主要原因就是并非所有计算都能够被增量的处理。一个很简单的例子就是求中位数:并没有增量算法保证精确求出中位数值。因此,当面对一些及其复杂的场景时,流处理系统难以胜任。** 计算成本。** 流处理可以大幅降低实时计算的成本。但这并不意味着,流处理在任何场景下都能够比批处理更具成本优势。事实上,在对计算结果新鲜度不敏感的场景中(比如财务报表统计等) ,批处理才能更加节约成本。这是因为,为了在数据进入系统时便进行增量计算,流处理系统不得不持续维护计算状态,消耗资源。相比之下,批处理在只有用户请求到达时才进行计算,自然在无需实时结果的场景下节省成本。
流处理与批处理的融合
讨论了这么多,相信大家也看出来,流处理与批处理各具特点,很难在全场景中实现完全替代。既然这两种处理模式会共存,那很自然有些人会想到在同一套系统中同时支持流处理与批处理。不少系统已经进行了一些探索,这里就包括了
目前阶段,
后记
在文章最开始的时候,我提到自己已经在流处理领域做了