自 1.8 版本开始,RisingWave 就支持将 SQL backend 作为元数据存储(meta store),用户可选择使用 PostgreSQL、MySQL 或 SQLite 来替代 etcd。而在即将发布的 2.1 版本中,RisingWave 将正式停止对 etcd 作为 meta store 的支持。使用 etcd 作为元数据存储的 RisingWave 集群升级到 2.1 时需要迁移到 SQL backend[1] 上。
那么,meta store 在 RisingWave 集群中究竟扮演着怎样的角色?使用 etcd 作为 meta store 时遇到了哪些问题?将 meta store 从 etcd 迁移至 SQL backend 又是如何解决这些困难,并带来了哪些显著优势?本文将引领读者深入探索 RisingWave meta store 的演进历程。
1RisingWave 的元数据存储
下面是 RisingWave 的架构简图,可以看到其内部有 frontend、compute node、compactor、meta server 等多种节点。
RisingWave 设计之初就决定拥抱云。为了充分利用云平台提供的弹性资源,云原生架构下,大部分节点都是无状态的节点。集群的持久化状态存储在各节点共享的存储系统上,分成了两部分:
在图下方的集群的持久化数据存储。这包括表和 MV 的数据,以及流计算的状态存储。这部分数据被组织成 LSM 树的格式,存储在对象存储服务(Object Storage Service,OSS)上,例如 AWS 的 S3 存储。 图右侧的集群的元数据存储(meta store),也是本篇文章的主角。这里存储了所有 Catalog 和用户信息、前面提到的 LSM 树的元信息和结构,以及所有流任务的拓扑结构以及其在计算节点上的分布。
meta server 节点直接读写元数据存储,是 RisingWave 集群的“大脑”,它和集群中的其他节点通信,实现集群的各种管理和变更功能,包括:
元数据管理:不同对象的创建、修改和删除。对于一些有对应流任务的对象,meta server 需要和计算节点交互,创建、修改和删除上面的流计算任务。 调度和管理计算节点上的计算任务,包括迁移,scale in/out 等操作。 发起和管理集群的 checkpoint。 管理 LSM tree,调度和发布 compaction 任务。
2使用 etcd 作为元数据存储
在最早,我们使用了 etcd 来作为元数据存储,出于方便维护和兼容性考虑,元数据被编码为 protobuf。作为饱经考验的元数据系统,etcd 很好地满足 RisingWave 对元数据存储高可用、持久化和事务处理的需求。但随着 RisingWave 的不断演进,用户集群的规模不断增长,我们开始遇到很多问题。开发复杂度高
etcd 作为一种键值存储,采用 KV 模型,虽然能够很好满足 Catalog、权限管理等结构简单、层次明确的元数据需求,但在处理复杂场景时显得力不从心。它无法原生支持多个字段到同一条记录的索引,如同时通过表名和表的 ID 高效查询表的元信息。此外,etcd 本身并不具备数据处理能力,一个需求可能需要通过多次查询才能满足。
例如,对于流计算图的维护和更改,etcd 的 KV 模型显得捉襟见肘。在计算图中,一个常见的需求是根据流计算任务的依赖关系,判断这些任务是否构成环路。如果直接在 etcd 上实现这一功能,meta server 需要反复迭代查询 etcd,这样的时延是无法接受的。
为了应对这一挑战,我们在 meta server 的内存中全量缓存了一份元数据。这样,在需要读取元数据时,可以直接访问内存,同时,在内存中也可以对元数据进行各种索引。然而,这份内存元数据的引入,除了对 meta server 的内存资源有比较高的要求之外,还增加了复杂度和开发者心智负担。开发者需要在更新 meta store 时手动维护缓存元数据,确保其与 etcd 保持一致。在 etcd 的事务提交成功或失败后,内存中的元数据也需要相应地进行提交或回滚。许多与 meta server 相关的 bug,都是由于内存元数据与 etcd 不一致,或者内存中元数据发生竞争或死锁导致的。
云上运维困难
etcd 通常面向的是裸机部署场景,而云厂商并未提供 etcd 的云上托管服务。因此,RisingWave Cloud 必须自行解决在云上部署 etcd 的问题。在短暂尝试开发 etcd operator 后,我们最终决定使用 Helm 在云厂商的托管 Kubernetes 服务(如 Amazon EKS)上部署 etcd。
在实际运维过程中,有状态的分布式存储给我们带来了不小的维护压力。许多工程师在解决 RisingWave 的线上问题时游刃有余,但面对 etcd 时却感到无从下手。常见的问题包括在大量请求后出现 OOM、etcd 未能及时进行 defrag 等。要彻底解决这些问题,往往需要深入探究并进行参数调优。另一方面,这些问题在一定程度上也与云上的磁盘性能略逊于本地磁盘有关。
种种挑战促使我们开始思考,在云上是否有更优的选择。
数据量上限
etcd 设计之初没有打算承担大规模的数据存储。这在设计上最明显的体现是其采用单 Raft Group 而没有实现 sharding。这使得其很多优化目标、实现、以及工业实践的使用都是在小数据规模下的。目前,其默认存储限制为 2 GB,推荐的最大数据量也只有 8 GB(参见文档[2] )。
起初,RisingWave 对单集群元数据的预估也不超过 GB 级别。我们原本设想,由于业务需求和维护成本的限制,单个集群内的流任务数量应该是有限的。然而,在实际应用中,我们发现有些用户通过脚本能够轻松创建和管理上千个物化视图。这个数字经过 SQL 的复杂度和并行度两重放大,产生了未预料的元数据大小,百万级的 Actor(RisingWave 中调度的最小单元)并没有开始预想的那么遥远。在 RisingWave Cloud 上,在 scale out 等元数据高频更新场景下由于 etcd 内 LSM 本身的放大,一些RisingWave 集群 etcd 的 4GB 磁盘很快耗尽。许多用户集群的元数据一直在持续增长当中,这让我们开始为未来考虑。
另一方面,对于大多数用户来说,他们的元数据规模其实非常小,每个集群都占用一个独立的 etcd 节点,造成了资源浪费。etcd 的不可扩展性也限制了元数据存储多租户部署的潜力。
其他问题
除此之外我们还遇到了一些问题,或大或小,体验不佳。例如:在磁盘耗尽恢复后,事务部分提交导致脏元数据。具体原因仍未查明。 采用 protobuf encoding 导致查错困难。用户需要编写脚本来查找所需信息。如果遇到脏数据,手动订正时同样需要借助脚本,这增加了操作的复杂性。 etcd 被设计用于处理较小的键值对。默认限制了 RPC 的大小在 1MB(参见开发指南[3])。而随着执行计划等元数据包含的字段越来越多,单条元数据的大小也在持续增大。UDF 等功能也带来了较大的单条字段。
3使用 SQL backend 作为元数据存储
面对这些挑战,我们开始考虑使用 SQL backend ,直接采用 OLTP 系统来作为 RisingWave 集群的元数据存储。这不仅能满足高可用、持久化和事务处理的基本需求,还能解决前文提到的问题。具体来说,使用 SQL backend 作为元数据存储有以下优点:
在开发复杂度方面,使用 SQL backend,开发者可以为元数据表创建索引,并且可以用 SQL 语句来表达原本在 meta server 上的查询逻辑。这样一来,meta server 上的缓存就不再需要了。例如,前文提到的找环逻辑,在 SQL 中只需一个 recursive CTE 语句就能轻松表达,所有的计算都将在作为 Meta Store 的数据库上进行。 在云上部署方面,RisingWave Cloud 可以利用云厂商托管的数据库服务。RDS 等各类数据库服务始终是各家云厂商的核心产品,因此可以获得良好的支持。 在数据量限制方面,现代的关系型数据库普遍可以支持 TB 级别的数据,用来存储元数据,大大扩展了 RisingWave 的应用范围。对于云厂商的 RDS 服务也在这个级别,例如 AWS RDS PG[4]就支持最高 128 个 vCPU 和 4096 GiB RAM,64TiB 存储 此外,使用 SQL backend 为多租户共享元数据存储提供可能。元数据的数据量和并发度通常都比较有限,对 OLTP 数据库来说是轻负载,多个 RW 集群共享同一个数据库做共享元数据存储可以减少资源浪费。不过,共享机制在元数据存储故障时影响范围扩大,我们正通过更多测试与实验来权衡其利弊。
4结论
迁移到 SQL backend 上: https://docs.risingwave.com/docs/current/migrate-to-sql-backend/
[2]文档: https://etcd.io/docs/v3.5/dev-guide/limit/
[3]开发指南: https://etcd.io/docs/v3.5/dev-guide/limit/
[4]AWS RDS PG: https://aws.amazon.com/cn/rds/features/
关于 RisingWave
往期推荐
技术内幕