1. 引言
数据库领域顶会 ICDE 2024于5月13-17日在荷兰乌特勒支(Utrecht, Netherlands)举办。ICDE (The International Conference on Data Engineering) 与VLDB、SIGMOD被公认为是国际数据管理领域三大顶级学术会议,此次在荷兰召开的ICDE 2024大会,共吸引北京大学、清华大学、浙江大学、MIT、斯坦福等机构,以及谷歌、微软、阿里云、华为、字节等公司的近1000名人员参会,共同探讨AI、数据库、数据处理领域的前沿技术问题。
阿里云数据库事业部共有3篇论文被ICDE 2024接收,其中《Towards a Shared-storage-based Serverless Database Achieving Seamless Scale-up and Read Scale-out》荣获工业和应用赛道的“最佳论文奖”(Industry and Application Track Best Paper Award)。本文将重点解读这篇获奖论文,该论文介绍了阿里云自研云原生数据库PolarDB Serverless的核心技术。
2. 论文背景
由于Serverless数据库可以提供高弹性并且支持按需付费,最近几年Serverless数据库变得越来越流行。Serverless数据库可以根据用户负载动态地细粒度地调整数据库资源,用户只需要为实际使用的资源付费。这种方式可以为用户节省大量费用。目前主流的商业Serverless数据库(如Aurora Serverless)主要是采用基于共享存储的主从架构。在该架构中,主从节点共享一份数据。所以,因此,新增从节点时不需额外的存储开销。由于只有主节点可以处理写请求,主节点在处理写请求时会生成redo日志并传输至从节点,从节点通过回放redo日志更新其内存中的数据。由于redo日志的传输和回放均为异步操作,从节点的内存数据可能滞后于主节点,导致从节点在处理读请求时可能返回过期的数据。因此,对于需要保证写后读一致性的读请求必须要在主节点上处理。
尽管基于共享存储的主从架构非常流行,但是基于这类架构构建Serverless数据库仍然不是一件简单的事。这篇论文通过分析当前基于共享存储的主从架构的Serverless数据库,提出了构建这类Serverless数据库所需要解决的两个关键问题。
3. 基于主从架构的Serverless数据库的关键需求
3.1 无感的跨机迁移
主从架构的数据库中,由于只有一个主节点可以处理写请求,所以当需要提升写能力时,需要向上弹升主节点。弹升主节点可能会面临一个问题,当前物理主机上没有足够的空闲资源来完成当前实例的向上弹升。这个时候需要将该实例迁移至另外一个有足够空闲资源的物理主机,以完成向上弹升。然而,实例迁移通常导致服务暂时不可用,这在应用的高峰期尤其不可接受,特别是向上弹升通常发生在应用的业务高峰期。为了避免实例迁移或者降低触发迁移的概率,一部分Serverless数据库会给物理机预留大量的空闲资源以应对潜在的迁移,但是这样会造成资源的浪费。还有一部分Serverless数据库通过限制单个实例的最大规格来减少迁移触发的概率,但是这样就违背的Serverless的基本原则,无法在应用业务压力变大时,弹升实例以提供更高的吞吐。
由于Aurora Serverless是非常流行的Serverless数据库,并且比其他Serverless数据库有更好的性能。所以用Aurora Serverless作为例子,测试了实例迁移的性能。由于难以在实验环境下人为地触发实例迁移,所以通过主备切换来模拟实例迁移,因为实例迁移也通常是先增加一个从节点,然后把该从节点切换成主节点。下图展示了实例迁移时的性能变化,可以发现整个迁移过程用了15秒左右,在这期间应用的连接全部中断,实例不可用直到迁移完成,由于向上的弹升通常发生在应用的业务量增加时,如果这个时候发生一段时间的实例不可用,对用户是难以接受的。所以我们认为,Serverless数据库需要实现无感的跨机弹性,在跨机迁移时不会对应用造成任何异常,并且迁移过程能够快速完成。
3.2 读扩展的需求
在基于共享存储的主从数据库中,从节点只支持最终一致性。因此,基于共享存储的Serverless数据库必须提供两个endpoint:一个确保强一致性,另一个提供最终一致性,通常给报告类应用使用,如下图所示:
在这种设计中,当读压力加重时,必须要扩展主节点而不是扩展从节点。由于分配给一个实例的资源通常有一个上限,读压力变大时(需要强一致性的情况)可能将主节点扩展到最大规格,从而导致性能无法进一步提高,同时从节点节点的扩展能力并未得到利用。我们再次使用Aurora Serverless作为例子展示在以读为主工作负载下从节点的自动扩展能力,如下图所示。在180秒时,由于需要强一致性,在主节点上启动了大量的只读工作负载。随后,实例分配的资源(ACU)的数量开始增加,最终稳定在其最大容量128,同时,这段时间QPS也在一直增加,最后稳定。我们继续增加额外的工作负载。然而,ACU的数量不再增加,因为它已经达到了其最大限制,进而吞吐量也没有进一步改善。如果这类数据库扩展其对从节点的强一致性支持,则可以通过扩展从节点来处理更重的读工作负载,从而提升性能。
4. PolarDB Serverless的整体架构
PolarDB Serverless是基于PolarDB构建的,因此与PolarDB有类似的架构,如图5所示,采用了分离的共享存储架构。同样地,PolarDB Serverless也有一个代理节点,该节点位于主节点和从节点之上。代理节点提供读写分离和负载平衡等功能,以及各种与Serverless相关的功能,包括连接管理和查询语句缓存。PolarDB Serverless以PolarDB Capacity Units(PCUs)为单位管理资源,其中一个PCU代表1个vCPU、2GB内存及相应的网络和I/O资源。PolarDB Serverless资源分配和回收的粒度为0.5个PCU。值得注意的是,PolarDB Serverless相对其他Serverless数据库有两个创新的特点:(1)PolarDB Serverless支持无感的跨机迁移,可以在0.5秒内完成实例迁移,且对应用完全透明。(2)从节点支持强一致读,可以支持从节点的横向扩展来提升读性能。
PolarDB Serverless继承了PolarDB的强一致特性,所以可以在从节点上支持高性能的强一致读,PolarDB的高性能强一致读在VLDB 2023的论文《PolarDB-SCC: A Cloud-Native Database Ensuring Low Latency for Strongly Consistent Reads》中已经介绍过了,论文解读的文章可以参考:《VLDB顶会论文解读|PolarDB MySQL高性能强一致集群核心技术详解》。因此,本文不再详细介绍PolarDB-SCC相关的内容,而是主要介绍无感跨机迁移的相关技术。
5. PolarDB Serverless的设计和实现
5.1 连接保持
连接保持是实现无感跨机迁移的一个重要基础,其目标是在实例迁移期间保持应用的连接不中断,并无感地将用户连接从旧实例迁移到新实例,使用户无法察觉到迁移发生。PolarDB Serverless利用了代理节点来实现这个功能。代理节点负责管理连接访问。所有应用程序都直接连接到代理节点。代理节点验证应用程序连接的访问权限,并为它们建立对应的数据库实例连接。在数据库迁移期间,代理节点会建立到新实例的新连接,并在退出旧实例后切断与旧实例连接。随后,将应用程序的连接重新映射到新实例。因此,应用程序与代理节点之间的连接保持活跃,没有任何中断。
5.2 事务续传
连接保持只是无感跨机迁移的一个基本要求,无感跨机迁移的一个最重要的需求是能够将活跃事务迁移到新的实例上。假设有这样一个事务,包含3个query。如果迁移发生在query2的执行过程中。那么迁移后需要在这个新实例上继续这个事务,需要恢复原事务已经完成的修改,并进行后续的query执行,在这个例子中query 1已经完成了并返回给了应用,但是query 1的修改可能还没有持久化到存储。所以在新实例上需要恢复query1的修改。迁移时query2正在执行,query2的部分修改可能已经被持久化了,所以在新实例需要回滚query2的修改,并重新执行query2。
基于上面的分析,为了实现活跃事务的迁移,我们在代理节点上缓存了每个事务最近的query语句以及已经完成的query所产生的最大undo id,如下图所示:
当代理节点收到一个query时,会先将query缓存,并标记为未完成的状态。随后query会被发送到数据库实例,数据库在处理query时会产生相应的undo和redo日志,在处理完query后,数据库会给代理节点返回结果,同时会将该query执行产生的最大的undo log id返回给代理节点。代理节点会缓存这个undo log id并将query执行结果返回给客户端,同时会将这个query标记为完成。这个代理节点缓存的undo log id表明了,当前事务在这个undo log id之前的修改已经响应给客户端了。在迁移到新实例时,通过回滚最新的undo log到这个undo log id就可以回滚所有没有响应给客户端的修改。
在发生实例迁移时,通常会先提前准备好目标实例,并将老实例上的redo log以及相关的一些元数据通过RDMA网络传输到目标实例,在这个过程中,老实例还可以继续处理请求,但是这个过程中老实例产生的所有redo及相关的元数据修改都需要同步到新实例,当新实例和老实例的数据同步好后,会将老实例停掉,在新实例可以给应用提供服务之前,新实例需要先恢复事务状态。首先新实例,通过回放redo log可以恢复老实例上所有的更新,包括数据更新和undo数据。随后,代理节点会检查其缓存内容,找出未完成的query。需要先回滚这部分未完成的query的修改,然后再重新执行这部分query。对于未完成的query的回滚,通过回放undo log来实现,从对应事务的最新undo log一直回放到代理节点上缓存的undo log id的。等回放完成后,代理节点会将未完成的query重新发给新实例,新实例就可以继续之前的活跃事务。
5.3 行锁状态迁移
PolarDB Serverless和大多数数据库一样,利用MVCC进行并发控制,并默认采用read committed snapshot isolation。这种情况下,读请求通常不需要加锁,写请求需要对对应的行加互斥锁,并遵循两阶段加锁协议。对于加锁,PolarDB Serverless采用了一些主流数据库(如Oracle和MySQL等)相似的做法,通过在对应行上存入当前事务ID来对行进行加锁,如果某一行的对应事务ID是一个活跃的事务,则该行当前被其他事务锁住了。所以,在跨机迁移时,如果在新实例上恢复了最新的数据,则对应的行锁状态也都恢复了。
5.4 binlog的迁移
PolarDB Serverless同样支持binlog。在跨机迁移时同样支持将老实例上的内存中的binlog迁移至新实例。具体细节由于文章篇幅原因,这里不具体展开,可以参考论文原文。
5.5 迁移过程的加速
事务迁移可以避免向应用抛出事务中断的异常,并降低在新实例上回滚未提交事务的相关开销。然而,进一步降低迁移开销的重要性也是至关重要的。PolarDB Serverless使用两种策略加速迁移过程。首先是基于RDMA的数据迁移。RDMA网络已经是阿里云的基础设施,与PolarDB高度协同设计。在迁移过程中,相关的内存数据(例如redo日志、binlog和必要的元数据)通过RDMA接口传输到新实例。另一种是提前预热新实例,包括从存储中预加载必要的日志数据,并在可能的情况下提前解析它们。这可以在迁移过程中节省大量I/O开销和CPU开销。
6. 实验分析
PolarDB Serverless已经阿里云公有云正式售卖,我们的实验也是直接在公有云购买实例直接测试,并和Aurora Serverless进行对比。
6.1 跨机迁移性能
我们首先测试跨机迁移的性能,仍然用主备切换模拟跨机迁移。结果如下图所以,在30秒左右我们进行了主备切换,切换过程PolarDB Serverless只用了0.5秒就完成了,但是Aurora Serverless用15s左右。
并且,在切换过程中,PolarDB Serverless并没有对应用抛出任何异常,整个过程完全透明,应用完全不感知后台的切换。但是Aurora Serverless在切换时会中断所有活跃事务,并给应用抛出异常,并且应用到数据库的所有连接都会中断,只有当新实例完全启动后,应用才可以连接实例。
6.2 读扩展能力
进一步测试PolarDB Serverless的读扩展能力。同时在PolarDB Serverless和Aurora Serverless上运行读为主的高压力负载。Aurora Serverless的吞吐会逐渐缓慢增加,然后饱和,这是因为Aurora Serverless的从节点无法保证强一致读的特性,只能在主节点处理所有读请求,读压力变大时,只能一直向上拓展主节点,当主节点规模达到最大值时就无法进一步扩展,最终性能也就稳定了。然而,PolarDB Serverless的从节点可以提供强一致的读,所以PolarDB Serverless可以通过拓展从节点处理大量读请求,最终PolarDB Serverless可以实现1M左右的QPS,这个是Aurora Serverless峰值的6.5倍左右。
更多的实验分析这里就不具体展开了,可以参考论文原文。同时,PolarDB Serverless的使用指南和相关的一些测试也可以参考之前的文章《PolarDB Serverless弹性能力探索指南》。
7. PolarDB Serverless的商业化情况
PolarDB Serverless自从2023年5月份商业化以来,用户量一直保持高速增长,目前总实例数突破1万个,按年增长率突破12倍。目前Serverless逐渐成为PolarDB售卖的主要形态。
作者:陈浩、章颖强