流计算引擎与流数据库:从设计到场景再到未来

作者: 吴英骏 RisingWave 创始人 & CEO

在实时分析领域中,如 Apache StormApache FlinkApache Samza 等一批优秀的流计算引擎在过去的十多年间相继涌现出来,被诸多企业运用在了生产中以支持在线业务。而在过去的几年间,一个看似全新的物种,流数据库,被创造出来。从早期的基于 PostgreSQL 插件做出的 PipelineDB,到之后流计算平台巨头 Confluent 专为 Kafka 打磨的KsqlDB ,再到现在的开源分布式产品 RisingWave,流数据库这个概念逐步被大家所接受,并在市场上得到普及。

那么,流计算引擎是否是跟流数据库互为替换关系呢?在本文,我就从两者的设计理念聊起,再详细讲解一下场景以及未来发展的异同。

注意:本文主要讨论流计算引擎与流数据库两大类别的异同,而不讨论特定系统的特定功能或特定发展方向。流计算领域发展迅猛,越来越多的系统涌现出来,本文也无法覆盖到所有所有系统。如有疏漏,还望见谅。

设计理念

流计算引擎与流数据库都是被用于进行数据流处理,而他们的设计理念却有诸多不同之处。其中,最重要的有两点:用户交互接口数据存储。在深入讨论设计理念前,我们先谈谈历史。

流计算引擎的历史

数据库的历史源远流长,而计算引擎(无论是流计算还是批计算)的历史从 Google 的MapReduce 论文发表的 2004 年开始算起,也即将步入第 20 个年头。

流计算引擎的诞生很大程度上受到了 Google 推出的 MapReduce 的启发。

MapReduce 最初要解决的问题是,如何在一堆普通计算机(commodity machine)上暴力使用资源来最大化性能。而要去充分利用资源,MapReduce 提出的方法是,将底层编程接口封装成 Map 和Reduce 函数之后,便直接暴露给有编程经验的用户,让用户自己实现具体业务逻辑,并自己可以操控程序并行度等细节。同时,MapReduce 不负责数据存储,所有数据都放在外部系统,如远端的分布式文件系统,中进行保存。

MapReduce 的想法要得以落地,需要有三个先决条件:

  1. 公司内部需要有足够多的数据以及业务场景;
  2. 公司内部需要有足够多的机器;
  3. 公司内部需要有足够优秀的软件工程师。

这三个条件在 MapReduce 提出的 2004 年是难以被大多数公司满足的。直到 2010 年之后,随着社交网络以及移动互联网的逐步爆发,条件 1 得以被满足,各大公司才重视起流计算这一技术,并花大价钱来满足条件 2 与 3。

这也造就了 2010 年左右开始的流计算引擎的黄金发展时期,一批优秀的流计算引擎在当时涌现出来,包括 Apache Storm、Apache Samza、Apache Flink 等等。

当时涌现出来的流计算引擎都充分吸收了 MapReduce 设计模式的精髓,即:A)暴露给用户底层编程接口 ;B)不再掌控数据存储。这种设计非常适合大数据时代。这是因为,在当时,对大数据计算有需求的科技公司多数都拥有自己的数据中心、自己的专业化工程团队,他们需要的是更极致的性能以及更灵活的编程模式。与此同时,归功于专业化工程团队,这些公司通常也能够自己部署分布式文件系统来存储海量数据。

而随着 2015 年之后云计算技术的飞跃发展,更多没有太多技术积累的公司也希望引入流计算技术。为了满足这一需求,流计算引擎纷纷选择了支持 SQL 这种简洁且通用编程语言,来让更多人享受到流处理这一技术。也正因为如此,我们可以看到,当今主流的流计算引擎所提供的是层次化的用户交互接口,即既有底层 Java、Scala 等语言编程接口,又有高层 SQL 编程接口。

用户交互接口

如上面所提到的,如今的流计算平台提供了 Java、Scala 等底层编程接口,并将系统运行时的细节(例如并行度等)暴露给用户。在此基础之上, 流计算平台封装了更高阶的 SQL、Python 等接口。

而流数据库提供的主要是 SQL 接口,并掩藏运行时细节;为了提高表达能力,一些流数据库可能会提供 Python、Java 等语言的 UDF(用户定义函数)。

用户交互接口的不同体现出来的是两方面的平衡。

第一个平衡是灵活性与易用性之间的平衡。

提供 Java、Scala 等底层接口很显然是给了对熟练掌握编程的工程师们以极大的表达能力。作为图灵完全语言,Java、Scala 等编程语言从理论上可以让用户表达出任意逻辑。同时,对于有 Java、Scala 库基础的公司来说,在程序中调用现有库更加方便。

而随之带来的便是对易用性的极大挑战。毕竟,很多非科班出身的用户并不了解如何使用Java、Scala,而即便科班出身,学习一个系统的自定义 API 很显然也要花去不少时间。

在底层接口上提供更高阶的 SQL 接口尽管部分弥补了系统在易用性上的不足,但还是有两方面难以逾越的鸿沟。

一方面是对 SQL 支持的完整度。毕竟当用户使用一个 SQL 系统时,并非只是想使用简单的select、insert 等 DML(Data Manipulation Language)语句,而是希望能够创建删除 role、user、index、view 等 DDL(Data Definition Language)语句,并进行各种更加复杂的 SQL操作。

另一方面是对 SQL 生态的支持。也许 SQL 语法的完整度可以通过不断添加功能来提升,而对SQL 生态的支持则需要额外的工作。比如,当用户使用 PostgreSQL 数据库时,习惯往往是通过 DBeaver 或者 pgAdmin 来进行系统管理。缺少了这样的管理工具支持,在企业中引入系统的门槛就会变高。

总的来说,提供底层接口的计算引擎给予专注于技术的用户在逻辑表达能力上以极大空间,而提供 SQL 接口的数据库给对专注于业务的用户以更好的易用性。

第二个平衡是灵活性与性能之间的平衡。

提供底层编程接口且暴露越多运行时细节给用户,用户就能更好的根据自己的先验知识提升系统性能。当用户有丰富的系统编程经验且对业务逻辑非常熟悉时,往往能够通过底层接口调出很好的性能。当然,这也意味着公司可能需要投入更多的资金来建设自己的工程团队。

而当用户使用的是高层编程接口时,提供层次化接口的计算引擎却很有可能在性能上逊于只提供 SQL 接口的数据库。毕竟,尽管性能的好坏还是非常依赖于实现,但计算机的基本原理告诉我们,封装越多,性能越差。在计算引擎中,中间表达层的引入往往会导致底层设计无法感知上层逻辑,导致性能衰减。而在数据库中,通过优化器与整体感知能力,使得性能的上界变得更高。

总而言之,就是暴露底层细节的计算引擎给予专注于技术的用户以提供先验知识的机会,从而在性能上有更大的发挥空间,而提供 SQL 接口的数据库对专注于业务的用户以更高的性能上界。

数据存储

除了用户交互接口之外,流计算引擎与流数据库之间的最重要的区别便是系统是不是存储数据。而在流处理中,是否带存储也直接关系到了系统的性能、故障恢复、可扩展性以及使用场景等方方面面。

在一个流计算引擎中,数据的输入与输出一般都是在外部系统中存储,如 HDFS 这样的远端分布式文件系统。而对于流数据库来说,其在拥有计算能力之外同时也拥有存储能力。这意味着其能够做至少两件事:1)存储输入;2)存储输出。

在数据输入端,存储数据带来的最大好处是对性能的提升。我们考虑在一个朴素的流计算引擎中 join 一个来自于消息队列(如 Kafka)数据流与存放在远端数据库(如 MySQL)中的一张表格。如果一个流计算引擎不带存储,那么带来的巨大问题便是,每次数据流中有一个新的数据流入,那么流计算引擎便不得不去远端数据库捞数据,再进行计算。而最早期的分布式流计算引擎几乎都是采用了这种方式。这种方式最大的好处便是简化系统架构,而随之带来的问题便是性能的剧烈下降。毕竟,跨系统访问数据很显然会造成高延迟,而在延迟敏感的流计算引擎中引入高延迟操作,很显然会造成性能的衰退。

在流处理系统内部直接存储(或缓存)数据带来的一大好处便是规避跨系统数据访问,提升性能。在流数据库这种自带存储的流处理系统中,如果用户需要 join 消息队列中的数据流与远端数据库的表格,则可以直接将存放在远端数据库的表格直接复制到流数据库中去,这样一切访问都会变成系统内部操作,实现了高效处理。当然,这只是一个最简化的例子,在实际场景中可能会遇到远端数据库中的表格过大或者会实时变化等情况,我们就不在这里详细展开了。

在结果输出端,存储输出结果带来的好处也许更令人兴奋。总结一下,大致有以下四点。

第一,简化数据栈架构。

一种最朴素(但并不完全正确)的看法是,流数据库是流处理引擎加上数据库。当用户使用流数据库进行流处理之后,可以直接使用同一系统来存储数据。相比于之前用单独的流计算引擎进行计算、用单独的存储系统进行存储与响应查询,使用单一系统,即流数据库,来做掉计算、存储与响应查询,很显然是对数据栈的简化。同时,用户也不再需要为多套系统付费,也是对成本的一种节约。

第二,实现计算资源共享。

对于流计算引擎来说,计算引擎在消费输入数据并进行计算后,会将计算结果导出到外部系统中,而这意味着计算结果无法直接在系统内部被复用。而对于带着存储的流数据库来说,计算产生的结果可以直接以物化视图的形式保存在系统内部,而其他计算可以直接访问物化视图中的结果来实现计算资源的复用。

第三,保证数据一致性。

当使用流计算引擎时,流计算引擎尽管能够保证精确一次性(exactly once)语义,但并不能保证结果访问的一致性。原因很简单,因为流计算引擎并不拥有存储,而计算结果必须导入到下游存储系统中。如果下游存储系统希望保证用户永远看到一致性的结果,那就必须要求上游的流计算引擎将结果的版本信息输出到下游系统中去。也就是说,用户必须对一致性负责。而对于拥有存储的流数据库来说,计算的进度与结果的版本信息都可以在同一个系统中管理,用户无需感知版本,而流数据库通过多版本控制来保证用户看到的永远是一致的结果。

第四,提升程序可解释性。

在用户开发程序的过程中,几乎不可避免地需要对程序进行反复修改,并验证正确性。而验证正确性的最好方式便是获取输入输出,并手工验证计算逻辑是否符合预期。这一做法在批计算引擎中相对简单,而在流计算引擎中并没有那么容易。这是因为流计算引擎中的输入输出是动态变化的,而流计算引擎并不拥有输入输出,因此当需要验证正确性时,必须横跨上游消息源、下游结果存储系统以及流计算引擎三个系统,并且用户需要感知计算进度信息,难度可想而知。而在流数据库中,由于计算结果也保存在计算引擎内部,因此验证结果的正确性相对简单:用户只需要从上游系统中获取输入(一般直接拿到如 Kafka 这样的消息队列中的 offset 即可),就能在单一系统内部进行结果的验证。也正因为此,用户的开发效率可以很大的提升。

当然,天下没有免费的午餐,而软件开发也从来没有银弹。相比于流计算引擎而言,流数据库携带存储带来了架构、资源利用、一致性与用户体验等方面的诸多好处,而其牺牲的到底是什么呢?

我认为牺牲的很大一块在于**软件设计复杂度,**让我们回想一下 MapReduce 的设计。MapReduce 的理念之所以在 Oracle、IBM 等巨头霸占整个数据库市场的时候脱颖而出,是因为其很好的使用了简单的模型让大规模并行计算在拥有大量普通计算机的科技企业中变得可行。MapReduce 直接颠覆了数据库中存储与计算一体的理念,把计算独立抽出做成产品,让用户能够通过编程的方式让计算变得可扩展。也就是说,MapReduce 使用简化的架构实现了计算的大规模水平扩展,而其可用的前提条件是拥有高素质的专业用户。

而当做一个需要系统需要考虑存储时,一切都变得复杂了。为了让这个系统可用,系统的开发人员不得不全盘考虑高可用性、故障恢复、动态扩展、数据一致性等等问题,而为了解决每一个问题,我们都需要进行精妙的设计与实现。好在通过过去十多年的探索,大规模数据库与流处理的理论与实践均得到长足发展,因此现在的确是出现流数据库的好时候了。

应用场景

流计算引擎与流数据库在计算模型上面并没有本质区别,因而两者应用场景中有不少重合。但值得注意的是,两个系统在实际场景中所面向的终端用户上稍有区别:流计算引擎的终端用户更多的是机器,而流数据库的终端用户更多的是人

流计算引擎因为其无法存储数据,因此其几乎肯定需要对接下游系统来消费计算结果。而由于流数据库带有存储,并能够支持随机查询,因此人可以直接对流数据库进行操作。人与机器有着本质区别:机器更注重对于硬编码的程序进行高性能处理,而人更注重使用体验以及交互模式。也正因为此,流计算引擎与流数据库在功能、体验等诸多方面出现分化。

历史的倒车还是时代的洪流?

数据库与计算引擎从设计思路上来讲可谓是不同门派。

在学术圈内,计算引擎的文章更多的是发表在 OSDI、SOSP、EuroSys 等系统会议中,而数据库的文章更多的是发表在 SIGMOD、VLDB 等数据库会议中。不少朋友们应该也听说过计算引擎类的几篇著名文章惨遭数据库会议 n 连拒的趣事。

更为人所熟知的应该是数据库巨佬 David DeWitt 与 Michael Stonebraker(也是图灵奖获得者)在 2008 年发表的雄文《MapReduce: A major step backwards》,"怒斥" MapReduce是开历史倒车,相比于数据库毫无创新可言。这一切的背后其实是两种设计思想的冲突。

那么,流计算引擎与流数据库,谁是历史的倒车,谁又是时代的洪流呢?我认为,两者在未来 3-5 年甚至更长的时间内都有可能是并存的状态。计算引擎的设计理念是极致的性能与极致的灵活性,而这一理念的假设前提是拥有专注于技术的用户;而数据库的设计理念是优雅的用户体验以及高效的性能,而这一理念的假设前提是数据库内部的精细实现。计算引擎与数据库的设计都有道理,而且两者也有互相融合趋势。在流计算领域,各个流计算引擎添加SQL 编程接口,便是去弥补自己在用户体验上的短板;而各个流数据库添加 UDF 功能,也是在弥补自己在编程灵活性上的不足。

从商业角度上来看,流计算引擎偏向的是对流计算有高度定制化需求且具备专业工程团队的公司,而流数据库偏向的是对流计算定制化需求不那么高且更专注于业务的公司。而站在2023 年这个时间节点上,流计算相比于批计算来说还处于早期,一些拥有专业工程团队的公司可能并不太愿意为技术产品付费,而一些不具备专业工程团队的公司可能对流处理没有足够强的需求,从而形成了一个较为尴尬的局面。当然值得欣喜的是,流处理这一市场很显然正在经历肉眼可见的高速发展。Confluent 的成功上市,以及 Snowflake、Databricks、MongoDB 等数据库巨头对流计算的大力投入,都印证着市场的兴旺。

发展未来

流计算尽管经历了二十多年的发展,但从商业落地来看,相比于批计算仍处于相对早期。但流处理的发展路径似乎大家都有共识。对于流计算引擎与流数据库,尽管二者在提供怎样的用户接口以及是否应该拥有存储等方面存在不同的观点,但各个产品的发展似乎都认同同时支持流计算与批计算,而区别点更多在于如何将两种计算模式更好的融合。换句话说,业界所谓的"流批一体"似乎是一个共识。

具体如何实现流批一体,各家倒是有自己不同的观点,但大体上分为三大类。

第一类我们称之为"单一引擎"。

也就是在一个系统中使用同一个引擎支持流计算与批计算两种功能。 这种实现方式的好处在于实现起来思路相对比较清晰,而给出的用户体验也比较统一。当然缺点在于,流计算与批计算在实现的细节方面,比如优化器、计算方法等方面,都有显著区别,实现起来实际上还是需要对流计算与批计算分别处理才能达到最佳性能。

第二类我们称之为"双引擎"。

也就是说,在一个系统内部分别实现流计算与批计算两套引擎。这种实现方式的好处在于能够对流计算与批计算进行分别优化,而缺陷则是实现工程量巨大,对于工程团队的协同合作与优先级管理有着非常高的要求。

第三类我们称之为"缝合系统"。

也就是说,与其实现一套完美系统,不如拿着现有系统进行封装,并给用户提供统一体验。很显然,这一种做法工程量看似最小,但也最难给出很好的用户体验。毕竟,市面上的系统支持接口各不相同,哪怕都是 SQL,也可能使用的是不同的方言,因此,如何为用户屏蔽掉这些不统一,会是一个核心问题。当然,由于可能需要管理多套系统,如何让多套系统互相交互、互相感知,也会是非常棘手的工程难题。

总结

流计算引擎与流数据库在设计与实战方面有部分重叠,也有诸多不同。但是两者都趋向于与对应的批处理系统做融合。具体使用哪类系统,还是需要用户根据自身场景做出自己的判断。

关于 RisingWave

RisingWave 是一款分布式 SQL 流处理数据库,旨在帮助用户降低实时应用的的开发成本。作为专为云上分布式流处理而设计的系统,RisingWave 为用户提供了与 PostgreSQL 类似的使用体验,并且具备比 Flink 高出 10 倍的性能以及更低的成本。了解更多:

GitHub: risingwave.com/github

官网: risingwave.com

公众号: RisingWave 中文开源社区

相关推荐
Fool丶玄浅20 分钟前
【数据库系统概论】—— 关系数据库
数据库·数据库系统
DashVector1 小时前
如何通过HTTP API检索Doc
数据库·人工智能·http·阿里云·数据库开发·向量检索
SEO-狼术2 小时前
Enhance Security in Software Crack
数据库
计算机毕设定制辅导-无忧学长2 小时前
Redis 初相识:开启缓存世界大门
数据库·redis·缓存
Rverdoser2 小时前
redis延迟队列
数据库·redis·缓存
weisian1513 小时前
Redis篇--常见问题篇6--缓存一致性1(Mysql和Redis缓存一致,更新数据库删除缓存策略)
数据库·redis·缓存
中草药z4 小时前
【Spring】深入解析 Spring 原理:Bean 的多方面剖析(源码阅读)
java·数据库·spring boot·spring·bean·源码阅读
地球资源数据云4 小时前
全国30米分辨率逐年植被覆盖度(FVC)数据集
大数据·运维·服务器·数据库·均值算法
Ahern_4 小时前
Oracle 普通表至分区表的分区交换
大数据·数据库·sql·oracle
夜半被帅醒5 小时前
MySQL 数据库优化详解【Java数据库调优】
java·数据库·mysql