RisingWave 是一款采用云原生架构的分布式流式数据库,本文详细介绍其架构。
引用自官网的架构图:
从架构图上可以看出,RisingWave由四类节点构成:
所有持久化数据均存储在对象存储(S3、GCS、Azure Blob)中,实现了计算与存储的独立弹性扩缩容。如需了解整体概览,请参阅什么是RisingWave
以下为各类节点的简要介绍:
| 节点类型 | 简要描述 | 详细描述 |
|---|---|---|
| 服务节点(serving Node) | 解析与优化查询、提供即席查询的无状态节点 | 处理用户连接,执行即席查询,并以高并发、低延迟的方式将流式作业分发至流计算节点。 |
| 流计算节点(Streaming Node) | 流处理的核心引擎 | 执行流图,维护流式作业状态,并执行持续计算。 |
| 元数据节点(Meta Node) | 集群的中央协调器 | 管理所有数据库对象的元数据,并编排流式作业的生命周期,包括调度、检查点与故障恢复。 |
| 压实节点(Compactor Node) | 优化数据存储的后台节点 | 对基于 LSM 树的存储执行压缩操作,以提升读取性能并回收存储空间。 |
该节点作为兼容 PostgreSQL 的前端服务,负责处理用户请求并以高并发、低延迟的方式返回结果。psql 等客户端可无缝连接至该节点。
服务层存在两种执行模式:本地模式(local)与分布式模式(distributed)。系统会根据批查询的预估计算负载,自动选择其中一种模式。
当表通过
engine=iceberg指定时,将启用Iceberg 服务引擎,该引擎基于Apache Iceberg 表格式构建。数据以列存格式存储,以提升即席 OLAP 风格查询的性能。
引用自其官网的图片:
用户提交查询后,请求首先到达服务节点(Serving Node)。解析器(parser)将原始查询文本转换为抽象语法树(AST)。接下来,绑定器(binder)将 AST 中的查询元素匹配到实际的数据库对象,从而生成绑定后的抽象语法树(Bound AST)。在绑定阶段,表名被关联到其实际定义,通配符(*)也被展开为表内所有物理列。最后,优化器(optimizer)通过多轮优化处理该 Bound AST,生成批处理执行计划。
分片器(fragmenter) 将执行计划拆分为多个片段(fragments)。每个片段是一组执行节点的集合,它们共享相同的数据分布策略,以最大限度地减少数据混排(data shuffling)。
调度器(scheduler)随后管理拆分后计划的执行。它会为每个数据分区生成每个执行节点的多个实例,以实现并行计算。最终的混排(shuffle)操作将所有分区结果聚合到单个实例中,根节点(root node)执行必要的排序操作。最后,根节点将结果返回给会话。
流计算节点(Streaming Node)负责执行流式查询,包括管理作业状态以及执行聚合、连接等计算操作。
流式查询是指执行增量式、实时计算的查询。以一个普通的批查询为例:
SELECT COUNT(*) FROM t;
通过在查询前添加 CREATE MATERIALIZED VIEW,即可将其转换为等价的流式查询:
CREATE MATERIALIZED VIEW m1 AS SELECT COUNT(*) FROM t;
与仅执行一次不同,当上游关系(upstream relations)产生数据更新时,该查询会持续执行。例如,当表 t 收到一条 DML 更新操作 INSERT INTO t VALUES(1) 时,该更新会自动传播到物化视图 m1。m1 对应的流图会读取上一次的计数值,将其加 1,并物化这个新的计数值。你可以随时通过 SELECT * FROM m1 查询 m1 中的最新结果。
引用自其官网的图片:
流式查询会经历与批查询生命周期相同的初始阶段。在计划分片(fragmentation)之后,执行路径与批查询分道扬镳,执行计划会被发送至元数据节点(Meta Node)。
元数据节点会依据执行计划,在流计算工作节点(Streaming worker nodes)上调度作业,指示这些节点构建计划中指定的执行节点。这些执行节点负责处理过滤、连接、聚合及其他各类计算操作。你可以执行 EXPLAIN 语句来查看执行图的结构。
执行节点构建完成后,系统会触发历史数据回填(backfilling),以确保与上游数据源的一致性。回填完成后,流式作业将被创建,持续处理上游数据、物化更新,并将转换后的数据流传播至任何下游系统。更多信息请参阅RisingWave 流处理引擎概览
元数据节点(Meta Node)通过与 元数据存储(meta store)交互来管理集群元数据,元数据存储是元数据的持久化层。RisingWave 支持 Postgres、MySQL 和 SQLite 作为元数据存储选项。所有数据库对象均持久化在元数据存储中,而服务节点(Serving Node)会从元数据节点拉取数据库目录并在本地缓存,以服务查询。
此外,元数据节点管理流式作业的生命周期,包括作业创建、用于保证一致性的状态检查点(state checkpointing),以及最终的删除操作。
⚠️ 初次接触屏障与检查点?屏障(barrier)是一种周期性同步标记;检查点(checkpoint)是由屏障生成的全局一致性快照。默认情况下,RisingWave 每 1 秒生成一个屏障(
barrier_interval_ms = 1000)。详见数据持久化。
RisingWave 采用日志结构合并树(Log Structured Merge (LSM) Tree)存储模型,这意味着所有数据操作均以仅追加(append-only)方式处理,即便是删除操作也会以墓碑记录(tombstone records)的形式表示。存储采用分层设计,从 L0 到 L6(或更多层级)进行层级化组织。数据最初写入 L0,随后逐步被压实合并到更高层级(L0 → L1 → L2,依此类推)。因此,热数据(频繁访问)会保留在较低层级,而冷数据(较少访问)则会迁移到较高层级。
压实操作是保障存储读取性能的必要手段。写入 L0 的数据是无序的,但从 L1 层级开始,压实操作会对数据进行排序。要读取数据的一致性快照,需要根据访问键范围进行归并排序(merge-sort),因为数据的不同部分可能分布在 L0 到 L6 的不同层级中。如果数据在所有层级中碎片化分布,且 L0 中存在大量未排序数据块,归并排序的耗时会显著增加。反之,如果压实任务定期执行,L0 会保持较小且有序的状态。通过提前完成压实工作,可以最小化读取时所需的排序与归并操作,从而实现更快的读取速度。
不同类型节点之间的会有各种交互。
在 RisingWave 中,计算节点(Compute Node)可以是专用的服务节点(Serving Node)或流计算节点(Streaming Node),也可以是默认的混合节点,同时处理服务与流计算任务。
对象存储作为持久化层,我们使用 OpenDAL对不同存储后端进行抽象,包括 AWS S3、Google Cloud Storage (GCS)、Azure Blob Storage 以及 MinIO。
这种架构意味着 RisingWave 可存储的数据量无上限,同时允许在不扩缩存储层的前提下自由扩缩计算节点,因为存储容量几乎是无限的。
每个计算节点独立运行,为流处理与批处理作业提供高并行度。数据流被分区并哈希到虚拟节点(vnodes)。由于每个计算节点都拥有独立的虚拟节点集合,其计算任务与其他计算节点保持隔离。
当计算节点(Compute Node)的资源扩容时(例如从 1 核 CPU 提升至 8 核),数据摄入的并行度也会成比例提升,可将摄入速率从每秒 10 万条记录提升至每秒 80 万条记录。存储层写入压力的相应增加,往往会导致 L0 层级中未排序数据的大量堆积。
这种数据堆积会直接影响读取性能:在读取范围数据时,需要对新旧记录进行排序以解决冲突。这会同时影响查询服务与流计算性能,因为流计算有时需要读取状态以更新计算结果,或为有状态查询刷新算子缓存。
因此,通常建议将计算节点与压缩节点的规模比例维持在 2:1。例如,对于 4 核 CPU、16 GB 内存的计算节点,推荐使用 2 核 CPU、8 GB 内存的压缩节点。在写入密集型负载场景下(如每秒向存储写入 50 万行数据),建议将比例调整为 1:8—— 这种场景通常出现在历史数据回填阶段,此时数据摄入规模极大。更多信息请参阅如何诊断压缩瓶颈。
在 RisingWave Cloud 中,可使用无服务器压缩(Serverless Compaction,当前处于公开预览阶段)来自动扩缩压缩节点,简化压缩资源管理。
当计算节点(Compute Node)资源扩容时,元数据规模也会同步增长。例如,若将计算资源从 1 核扩展至 64 核,actor 数量会增加 64 倍。系统需要捕获这些新 actor 的元数据,这会对元数据节点(Meta Node)造成压力。
因此,通常建议将计算节点与元数据节点的规模比例维持在 4:1。例如,若计算节点配置为 8 核 CPU,推荐元数据节点配置为 2 核 CPU。
纵向扩容(Vertical Scaling)时,应保持 CPU与内存1:4 的配置比例。这是因为内存被有状态算子(stateful operators)用于维护缓存,以避免延迟惩罚;同时也用于缓存从对象存储(如 S3)拉取的 排序字符串表(SortedStringTable, SST)文件块。
若内存不足,磁盘缓存(disk cache)可作为内存块缓存与对象存储之间的中间层(三级存储)使用。