论文阅读-Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Database

Posted by keys961 on May 5, 2020

1. Introduction

Aurora是一个AWS上的分布式OLTP关系数据库,而本文的一个重要观点是:性能瓶颈从CPU和存储变成了网络,因此将计算与存储分离

  • 数据库实例(计算层)和存储层松耦合
  • 数据库实例(计算层)仍包含大部分核心功能,如查询、事务、锁、缓存、访问接口、undo日志等
  • redo日志相关功能下移到存储,如日志处理、故障恢复、备份还原等

这样的优势有:

  • 底层存储层本身是分布式存储服务,可应对故障
  • 数据库实例(计算层)只往下面存储层写redo日志,降低两层之间的网络压力
  • 部分核心功能(故障恢复、备份还原等)移到存储层,可后台异步执行,不影响前台任务

论文主要包括三大块:

  1. 如何在云上实现持久化,基于quorum模型,使存储层能弹性容错(第2节)
  2. 如何将计算层下层的日志处理功能转移到存储层上(第3节)
  3. 如何在分布式存储中消除多阶段同步,并避免开销巨大的崩溃恢复和检查点操作(第4节)

2. 可伸缩的持久性

本节介绍quorum模型,并介绍存储分段。

Aurora将两者结合,实现持久性、可用性、少抖动,并解决大规模存储的相关问题。

2.1. 复制及其相关的错误

大规模下,错误经常发生,所以需要复制以容错。

Auroma使用基于quorum的复制协议

  • 记副本个数为$V$个,读需要$V_{r}$个副本的投票,写需要$V_{w}$个副本的投票
  • 首先要保证必有一个副本是最新的,即$V_{r} + V_{w} > V$
  • 写需要有多数的同意,防止写冲突,即$V_{w} > V/2$

常用配置下,Aurora通常将副本分布到3个AZ(可用区),每个AZ有2个副本,即$V=6$,然后设置$V_{r}=3, V_{w}=4$。这样它可以:

  • 一个AZ整个故障,依旧不影响写服务(AZ)
  • 一个AZ整个故障,另一个AZ一个副本故障,依旧不影响读服务,且不丢失数据(AZ+1)

2.2. 分片存储

以2.1.的AZ+1故障为例,我们需要保证两个完整AZ故障的概率足够低,才能保证quorum有效。

而故障频率(MTTF)不可知,因此需要降低故障修复时间(MTTR),从而降低2个AZ同时故障的概率。

解决方式是分片存储:

  • 副本根据固定大小分片(如10GB)
  • 以2.1.为例,每个分片被6路复制到3个AZ上,每个AZ有2个,这6个分片组成一个保护组(PG)

Aurora存储由若干个PGs构成,而PGs由EC2+SSD存储节点构成。

分片后,每个分片就是一个故障单位,恢复时只要恢复指定分片即可,速度会非常快。这降低了MTTR,从而提高了可用性。

2.3. 弹性容错的运维优势

Aurora能对长短故障都有弹性,这基于quorum协议和分段存储。

因此,系统可灵活应对故障,且运维方便,如:

  • I/O压力过大:可剔除某个分段,通过迁移热点数据到冷节点上,quorum机制可很快地修复
  • 系统更新导致的不可用:可临时将节点剔除,待完成后再加入到系统
  • 软件更新:同系统更新

以上操作,都是分片粒度滚动进行,对外完全透明。

3. 日志即数据库

“日志即数据库”即:redo日志包含所有数据,因为它可回放。

即数据库就是redo日志数据流

本节介绍Aurora的计算存储分离架构是如何体现这个思想的。

3.1. 写放大问题

在传统关系数据库跨数据中心的复制场景下,会产生非常多的磁盘和网络I/O,性能糟糕。

下图以MySQL为例:

  • AZ1和AZ2各部署MySQL实例,进行同步镜像复制
  • 底层存储使用EBS,每个EBS还有一个自己的镜像
  • 另部署S3执行redo日志和binlog的归档,以实现基于特定时间点的恢复

1.png

那么一个写操作需要5步,1、3、5是串行同步的,因此性能很差。以分布式观点,它是一个4/4的write quorum(4个EBS镜像),写性能可见也是很差的。

3.2. 将Redo日志处理下放到存储层

传统数据库中:

  • 写操作不仅修改数据页,修改后同步产生redo日志项
  • 提交事务时,将redo日志刷屏成功才能返回

Aurora中:

  • 写操作只产生redo日志项
  • 存储层收到redo日志项,回放日志得到新数据页
  • 避免从头回放,存储层定期物化数据页版本

架构如下所示:

2.png

  • 三个AZ中,AZ1是主,其它AZ是从
  • (计算层)实例和存储层只传递redo日志和元数据
  • 写时主实例向6个存储节点发送redo日志,需要4个应答才认为日志持久化了
  • 回放日志可在后台就行,若读操作得到的是旧数据页,则会触发日志回放
  • 存储节点定期物化数据页,恢复时只需回放很少的日志,非常快速

3.3. 存储服务设计的关键点

Aurora设计的关键原则是减少前台响应时间,所以尽可能将操作在后台异步执行,并根据压力自适应分配资源。

下面是一个写操作的具体流程:

  1. 数据库实例(计算层)收到写请求,生成redo日志项,发送给存储节点,存储节点将日志项添加到内存队列中
  2. 存储节点本地持久化redo日志项,并响应数据库实例
  3. 整理日志,检查日志缺失
  4. 和其它存储节点gossip,填补日志缺失
  5. 回访redo日志,生成新数据页
  6. 周期生成快照,备份数据页和日志到S3系统
  7. 周期回收过期版本的数据页/快照
  8. 周期对数据页进行校验

整个过程中,只有1和2是前台串行的(会影响延迟),其它都是后台操作。

1.png

4. 日志延伸:一致性

本节介绍如何不用2PC提交协议下,如何生成redo日志,以保证持久化状态、运行时状态和副本状态的一致性。

具体如下:

  • 在崩溃恢复时,如何避免昂贵的回放开销
  • 解释常规操作,并解释如何维护运行时和副本的一致性
  • 提供恢复过程的具体细节

4.1. 异步日志处理

首先介绍一些概念:

  • LSN:每个redo日志全局唯一且单调递增的序列号
  • VCL:表示LSN在VCL之前,收到的日志在本存储节点是完整的
    • 故障恢复时,VCL之后的日志都要截断
  • CPLs:事务可分成多个最小原子的mini-transaction(MTR),每个都有对应的最后一条日志LSN,这就是CPL;一个事务有多个CPL,所以叫做CPLs
  • VDL:最大已持久化的LSN,满足
    • VDL $\le$ VCL
    • VDL是CPLs中最大的一个

从上面可知,日志完整性和日志持久化是不一样的。

易知VDL表示了数据库处于持久化一致的位点,所以在故障恢复时:数据库以PG为单位,确认VDL,截断大于VDL的日志。

4.2. 标准操作

a) 写

数据库实例向存储节点传递redo日志,收到足够的票数后,标记事务提交,VDL增加,从而进入新的一致状态。

不过由于系统并发执行很多事务,所以要避免VDL追不上LSN,有:

  • 定义并设置LAL,即LSN与VDL差值的上限,避免存储层成为瓶颈

而由于底层存储是分片的,每个PG/每个分片只能见到部分日志,为保证PG/分片上日志的完整性,有:

  • 每条日志有当前PG前一条日志的指针

  • 定义SCL,表示LSN在SCL之前,收到的日志在本PG是完整的,存储节点通过gossip填补缺失的日志,以推进SCL

b) 提交

提交是异步的:

  • 每个事务包含commit LSN,当VDL大于事务的commit LSN时,表示事务redo日志都已经持久化,则向客户端回复事务已成功执行
  • 处理回复是由单独线程执行的,因此整个提交流程不会阻塞

异步提交极大提高了系统的吞吐。

c) 读

读先从缓冲池获取,若缓冲池满才读盘,此时会挑选并淘汰数据页。

但是对于淘汰的数据页,Aurora直接丢弃,不会刷盘。所以需要保证被淘汰的数据页page LSN要不超过VDL,这就需要两个约束:

  • 数据页上的修改都已经持久化了
  • 缓存未命中是,通过数据页和VDL总能得到最新的数据页版本

对于读操作,正常情况下,不需要quorum:选定一个存储节点进行读取,它需要满足有最新VDL的数据。

d) 复制

写副本实例(主实例)可与至多15个读副本实例(从实例)共享一套分布式存储,因此增加读副本(从实例)不会消耗I/O资源,即零成本。

从上文可知,写副本会向读副本发送日志,读副本会回放日志,它需要满足,若不满足则忽略日志项:

  • 回放日志的LSN不超过VDL
  • 回放日志要以MTR为单位,确保副本能看到一致性视图

4.3. 故障恢复

常规数据库采用基于ARIES协议进行故障恢复:

  • 周期性做checkpoint
  • 从上一个checkpoint回放redo日志
  • 根据undo日志回滚未提交的事务

这需要检查点频率和故障恢复时间做权衡,但Aurora不需要。

Aurora虽然也采用类似的方法,但是基于计算存储分离以及分片的设计:

  • 存储层部分失效时,能快速恢复
  • 数据库实例宕机后:
    • 故障恢复只需要通过与存储节点read quorum,即可读到最新数据,并得到VDL以截断后面的日志
    • 由于LSN与VDL有差值限制,undo操作不会很多,且可在后台进行

5. 集成

整体在云上集成的架构如下所示:

3.png

  • 应用层:部署应用程序,与数据库实例交互
  • 数据库实例层:部署数据库实例(Aurora MySQL)
    • 数据库实例包含一个主实例(读写)和多个从实例(只读),位于同一个物理区域,跨AZ部署
    • 使用RDS管理元数据,每个数据库实例部署一个RDS HM,监控集群健康度,确定实例是否要切换或重建等
  • 存储层:部署存储节点
    • 存储节点尾与同一物理区域,跨AZ部署(如至少跨3个AZ)
    • 其中部分节点和S3相连,后台备份数据,必要时还原数据
    • 使用Amazon DynamoDB持久化存储配置、元数据信息、S3备份信息
    • 所有关键操作都会被监控,若性能和可用性不达标,则会触发警报

6. 总结

本文的主要贡献点在于:

  • 重提“数据库即日志”的观念
  • 基于quorum机制,和下放redo日志处理,使用“计算和存储分离”的架构,减少I/O开销,提高吞吐,可用性增加,且能保证一致性
  • 提出分片存储,降低存储层恢复的时间,提高可用性
  • 大部分关键操作异步化、后台化,极大降低前台任务的延迟