亚马逊AWS官方博客

保驾护航 – Amazon RDS for MySQL 5.7 到 8.0 升级优势

1. 前言

MySQL 是流行的开源关系数据库,Amazon RDS for MySQL 可以在云中更容易设置、操作和扩展 MySQL 数据库。使用 Amazon RDS,您可以在几分钟内部署可伸缩的 MySQL 服务器。Amazon RDS for MySQL 封装了繁重的数据库管理任务(包括备份、升级、软件补丁、性能改进、监视、扩展和复制),使您可以专注于应用程序开发。

据开源 MySQL 社区说明,首次发布于 2015 年 10 月 MySQL 5.7 版本在 2023 年 10 月迎来大版本的退役,社区会停止对该版本支持,不会再在该版本上增加新功能、打安全补丁和修复 Bug。为了和开源社区保持兼容和步调一致,Amazon RDS for MySQL 也会在 2024 年 2 月 29 日终止对 5.7 版本的标准支持,专注于新版本新功能的迭代。与此同时,面向有些客户对大版本升级的犹豫,Amazon RDS for MySQL 提供了最多三年直至 2027 年 2 月的扩展支持供客户选择。实际上,MySQL 8.0 相对于 5.7 版本做了很多的功能增强,比如窗口函数、通用表达式、不可见索引等等,Amazon RDS for MySQL 8.0 也开发了很多新的特性,比如多可用区集群、读写增强等。升级到 8.0 版本能够帮助您使用到新功能,提升性能,方便新应用程序的研发和现有数据库的运维调优,也能及时跟进开源社区后续的新功能和 Bug 修复的情况。

基于升级的需求,我们写了一系列博客, 《保驾护航 – Amazon RDS for MySQL 5.7 到 8.0 升级指南 》 提供对于升级的总体介绍,《保驾护航 – Amazon RDS for MySQL 5.7 到 8.0 升级前置检查》详细列出如何进行前置检查找到 5.7 和 8.0 版本不兼容的地方,《保驾护航 – Amazon RDS for MySQL 5.7 到 8.0 升级方案》 给出几种不同升级方案的选型和具体指导,《保驾护航 – 实战分享,通过 External Master 完成 Amazon RDS for MySQL 加密和升级 》给出一个实际同时升级和加密的案例。本篇博客的目的在于介绍 MySQL 8.0、RDS MySQL 8.0 以及亚马逊自研的 Amazon Aurora for MySQL 8.0 的典型新功能,方便您更好地对 MySQL 8.0 有一个详细的了解,做出符合您自身情况的升级决策。

2. MySQL 8.0 新功能

MySQL 8.0 的第一个版本于 2018 年 4 月推出,至今已过去 5 年的时间。在过去 5 年中,MySQL 8.0 开发了很多新功能,本节着重介绍几个用户喜爱的新功能。

2.1 Instant DDL

关系数据库需要在建表时指明确定表结构,比如列的信息,但随着用户业务的变化,有时会需要更改表结构,比如增加/减少一些列,或者为了查询性能增加索引、调整主键等。这些 DDL 通常会操作整张表,在运行时会进行锁表等操作,影响并发查询的性能。表越大,DDL 操作的时间越长,对业务影响越大。基于 DDL 的影响, MySQL 社区一直致力于实现 DDL 的无阻塞以及快速操作。MySQL 5.5 提出了 INPLACE DDL 的概念,优化了创建/删除索引的过程,避免表复制;MySQL 5.6 允许增加二级索引、在线改变自增值时运行并发 DML 操作等。MySQL 8.0 进行了进一步扩展支持:8.0.12 提出了 INSTANT DDL 的概念,可以支持更加快速的 DDL;8.0.29 支持快速向表的任意位置添加列以及 DROP 列等。

MySQL 8.0支持 online DDL 的使用举例如下:

ALTER TABLE tbl_name ADD PRIMARY KEY (column), ALGORITHM=INPLACE, LOCK=NONE;

ALGORITHM 支持几个取值:

  1. DEFAULT 表示 MySQL 自己选择锁定资源最少的方式
  2. INSTANT 只需要更新数据字典中的元数据,更改很快
  3. INPLACE 不需要创建临时表
  4. COPY 执行 DDL 时会创建临时表。

LOCK 支持几个取值:

  1. DEFAULT 表示 MySQL 自己选择锁定资源最少的方式
  2. NONE 支持 SELECT 和 DML
  3. SHARED 支持 SELECT,不支持 DML
  4. EXCLUSIVE 不支持 SELECT 和 DML

使用 INPLACE/INSTANT 算法能够允许一定情况下的并发 DML 语句,减少由于 DDL 对在线业务系统的影响,也一定程度上避免了 COPY 带来的双倍存储空间的占用。LOCK 能使用户在 INPLACE 情况下进行性能和并发读的抉择。如果指明的 ALGORITHM 或者 LOCK 在当前语句不支持,会直接抛错。

MySQL 8.0 目前支持 online DDL 的常见语句包括:

  1. 索引操作。更改二级索引类型(比如 Btree 或者 Hash 索引)支持 INSTANT 算法,允许并发 DML 操作;创建/增加二级索引、删除索引、重命名索引支持 INPLACE 算法,允许并发 DML 操作;增加全文/空间索引支持 INPLACE 算法,但不允许并发 DML 操作。
  2. 主键操作。不支持 INSTANT 算法;增加主键、同一条语句里删除又立刻增加主键通常支持 INPLACE 算法,允许并发 DML 操作;删除主键只支持 COPY 算法,不允许并发 DML。主键操作通常需要重新构建整张表。
  3. 列操作。增加/删除/重命名列(通常情况下)、更改/删除列的缺省值、更改 ENUM/SET 列类型的定义支持 INSTANT 算法,允许并发 DML 操作;对列重排序、扩展 VARCHAR 列类型的长度(0-255 之间,或者 256-511 之间等)、更改列的自增值、将列定义成空/非空等支持 INPLACE 算法,允许并发 DML 操作;但更改列的数据类型上述两种算法均不支持,必须进行表的重构。关于列的增加和修改,0.29 支持使用 INSTANT 的方式在表的任意位置增加列,MySQL 8.0.28 支持以 INSTANT 的方式重命名列。
  4. 生成列。增加/删除 VIRTUAL 列支持 INSTANT 算法,允许并发 DML;删除 STORED 列支持 INPLACE 算法,允许并发 DML;增加 STORED、更改 STORED/VIRTUAL 列的顺序两个算法都不支持,必须要重构表。
  5. 外键约束。不支持 INSTANT 算法。增加/删除外键约束支持 INPLACE 算法,允许并发 DML。其中,只有在 foreign_key_checks 被关闭时才允许 INPLACE 方式增加外键的约束。
  6. 表操作。重命名表支持 INSTANT 算法,允许并发 DML;更改 ROW_FORMAT/KEY_BLOCK_SIZE/STATS_PERSISTENT、指定字符集、Optimize 表、ALTER TABLE 来 rebuild 表支持 INPLACE 算法,允许并发 DML;更改表的字符集两种算法均不支持,不允许并发 DML。

下面子图列出了 INSTANT 算法支持的常见操作类型。

关于 Online DDL 的详细介绍,可以进一步参阅官方文档或者博客 RDS/Aurora MySQL 的大表操作和管理最佳实践之大表 DDL

2.2 通用表达式 Common Table Expression

通用表达式可以计算 SQL 语句中的中间临时结果并进行存放,后续语句中可以直接引用这个中间结果。它的执行范围是单个 SQL 语句。典型语法如下,通过 WITH 关键词来定义 CTE 并赋名,SQL 语句的后续部分 SELECT/INSERT/UPDATE/DELETE 语句可以直接调用。

WITH
  cte1 AS (SELECT a, b FROM table1),
  cte2 AS (SELECT c, d FROM table2)
SELECT b, d FROM cte1 JOIN cte2
WHERE cte1.a = cte2.c;

使用 CTE 有如下几个好处:

  1. 通过将中间结果单独定义,SQL 尤其是复杂 SQL 可读性更高,无需定义子查询,也能提高可维护性。
  2. 如果一个 CTE 在后续 SELECT 中被调用多次,只需要一次计算,多次调用,节省计算空间、提升计算性能。
  3. 递归 CTE 的支持。递归 CTE 可以更加轻松愉快地用优雅简洁的方式实现复杂的查询。下图是递归 CTE 的一个示例。
WITH RECURSIVE cte (n) AS
(
  SELECT 1
  UNION ALL
  SELECT n + 1 FROM cte WHERE n < 5
)
SELECT * FROM cte;

关于 CTE 的详细介绍,可以进一步参阅官方文档

2.3 窗口函数 Window Function

MySQL 8.0 增加了对窗口函数的支持。它是指对于查询中涉及到的每一行,会针对该行以及和该行相关的行一起进行计算。与普通聚合函数的区别在于普通聚合函数会将多条记录聚合成一条,而窗口聚集函数会为每一条记录都生成一条对应记录。

比如针对 sales 表的查询:

mysql> SELECT * FROM sales ORDER BY country, year, product;
+------+---------+------------+--------+
| year | country | product    | profit |
+------+---------+------------+--------+
| 2000 | Finland | Computer   |   1500 |
| 2000 | Finland | Phone      |    100 |
| 2001 | Finland | Phone      |     10 |
| 2000 | India   | Calculator |     75 |
| 2000 | India   | Calculator |     75 |
| 2000 | India   | Computer   |   1200 |
| 2000 | USA     | Calculator |     75 |
| 2000 | USA     | Computer   |   1500 |
| 2001 | USA     | Calculator |     50 |
| 2001 | USA     | Computer   |   1500 |
| 2001 | USA     | Computer   |   1200 |
| 2001 | USA     | TV         |    150 |
| 2001 | USA     | TV         |    100 |
+------+---------+------------+--------+

普通聚合函数的输出结果如下:

mysql> SELECT SUM(profit) AS total_profit
       FROM sales;
+--------------+
| total_profit |
+--------------+
|         7535 |
+--------------+
mysql> SELECT country, SUM(profit) AS country_profit
       FROM sales
       GROUP BY country
       ORDER BY country;
+---------+----------------+
| country | country_profit |
+---------+----------------+
| Finland |           1610 |
| India   |           1350 |
| USA     |           4575 |
+---------+----------------+

而窗口聚合函数的输出结果如下:

mysql> SELECT
         year, country, product, profit,
         SUM(profit) OVER() AS total_profit,
         SUM(profit) OVER(PARTITION BY country) AS country_profit
       FROM sales
       ORDER BY country, year, product, profit;
+------+---------+------------+--------+--------------+----------------+
| year | country | product    | profit | total_profit | country_profit |
+------+---------+------------+--------+--------------+----------------+
| 2000 | Finland | Computer   |   1500 |         7535 |           1610 |
| 2000 | Finland | Phone      |    100 |         7535 |           1610 |
| 2001 | Finland | Phone      |     10 |         7535 |           1610 |
| 2000 | India   | Calculator |     75 |         7535 |           1350 |
| 2000 | India   | Calculator |     75 |         7535 |           1350 |
| 2000 | India   | Computer   |   1200 |         7535 |           1350 |
| 2000 | USA     | Calculator |     75 |         7535 |           4575 |
| 2000 | USA     | Computer   |   1500 |         7535 |           4575 |
| 2001 | USA     | Calculator |     50 |         7535 |           4575 |
| 2001 | USA     | Computer   |   1200 |         7535 |           4575 |
| 2001 | USA     | Computer   |   1500 |         7535 |           4575 |
| 2001 | USA     | TV         |    100 |         7535 |           4575 |
| 2001 | USA     | TV         |    150 |         7535 |           4575 |
+------+---------+------------+--------+--------------+----------------+

OVER 关键词能指明窗口的范围,比如上面的 OVER()为空表示整表范围,而第二个 OVER(PARTITION BY country)则指明范围为同一个国家。

除支持普通聚合函数比如 COUNT()、SUM()、AVG()、MAX()、MIN()外,窗口聚合函数还支持一系列自己独特的方法,比如取值函数 FIRST_VALUE()、 LAST_VALUE()、NTH_VALUE()来获取窗口中特定行的值,序号函数 RANK()、DENSE_RANK()、ROW_NUMBER()来为窗口中的每一行生成一个序号,分布函数 PERCENT_RANK()、CUME_DIST()来计算窗口中每一行在整个分区中的相对位置,前后函数 LAG()、LEAD()来获取当前行的前后某一行的值,NTILE()函数来指明在 partiton 内继续进一步分成 bucket 的数目等。

使用窗口函数有如下几个好处:

  1. 可以更加优雅地按需进行某个 partition 上的聚合函数计算,无需创建临时表或者实图等,简化数据分析工作中查询语句的书写。
  2. 能在聚合方法中查看到详细每一行的信息,而不是聚合后的结果。
  3. 可以通过滑动窗口来处理动态数据范围,比如移动平均值、累计和等。
  4. 可以与普通聚合函数、子查询等结合使用来实现更复杂的查询逻辑。

对于同一个窗口内需要进行多个运算的情况,可以通过命名窗口来进一步简化 SQL 书写。

SELECT
  val,
  ROW_NUMBER() OVER w AS 'row_number',
  RANK()       OVER w AS 'rank',
  DENSE_RANK() OVER w AS 'dense_rank'
FROM numbers
WINDOW w AS (ORDER BY val);

关于窗口函数的详细介绍,可以进一步参阅官方文档

2.4 索引

MySQL 8.0 有两个针对索引的更新:降序索引和不可见索引。

降序索引

降序索引是以降序存储键值的索引。在 MySQL 8.0 之前,可以在索引定义中指定 DESC。但是 MySQL 会将它忽略。在进行索引扫描时,MySQL 可以以相反的顺序扫描索引,但成本很高。MySQL 8.0 支持降序索引,索引定义中的 DESC 不再被忽略,会按降序存储键值。用户可以通过自己查询的特征来定义索引的升降序。MySQL 8.0 的多值索引也允许升序索引和降序索引的混合定义,比如一些列是升序排列,另一些列是降序排列。

比如下面的四个索引定义在进行不同查询查询时选择的索引也是不同的。

CREATE TABLE t (
  c1 INT, c2 INT,
  INDEX idx1 (c1 ASC, c2 ASC),
  INDEX idx2 (c1 ASC, c2 DESC),
  INDEX idx3 (c1 DESC, c2 ASC),
  INDEX idx4 (c1 DESC, c2 DESC)
);

ORDER BY c1 ASC, c2 ASC    -- optimizer can use idx1
ORDER BY c1 DESC, c2 DESC  -- optimizer can use idx4
ORDER BY c1 ASC, c2 DESC   -- optimizer can use idx2
ORDER BY c1 DESC, c2 ASC   -- optimizer can use idx3

关于降序索引的详细介绍,可以进一步参阅官方文档

不可见索引

不可见索引可以用来评估索引的有效性。如果将索引设置为不可见,且变量 optimized_switch的use_invisible_indexes flag 为 off 时,MySQL 在生成查询计划时会忽略这条索引。

数据库索引本身是有双面性的。通常情况下,较多的索引能匹配多种查询条件的组合,从而带来性能上的提升,但较多的索引也会导致插入/更新/删除性能的恶化,因为除原始数据外,还需要操作索引数据,也会带来额外的存储空间消耗。因此,在优化数据库性能时经常考虑的一个方向就是优化索引。将索引设置成不可见,会使得优化器在选择执行计划时,忽略该索引。所以如果设置成不可见,查询性能并没有降低,我们可以考虑删除该索引。

用户可以在建表/索引时直接定义索引是否可见,也可以 ALTER 已有索引来更改其可见性。

CREATE TABLE t1 (
  i INT,
  j INT,
  k INT,
  INDEX i_idx (i) INVISIBLE
) ENGINE = InnoDB;
CREATE INDEX j_idx ON t1 (j) INVISIBLE;
ALTER TABLE t1 ADD INDEX k_idx (k) INVISIBLE;
ALTER TABLE t1 ALTER INDEX i_idx INVISIBLE;
ALTER TABLE t1 ALTER INDEX i_idx VISIBLE;

关于不可见索引的详细介绍,可以进一步参阅官方文档

除了上面描述的几个 MySQL 8.0 的新功能外,MySQL 8.0 还支持一系列的新功能,比如数据字典原子 DDL角色 RoleTempTable 存储引擎等等。由于篇幅有限,这里不再详细展开。您可以参阅官方文档

3. Amazon RDS for MySQL 8.0 新功能

Amazon RDS for MySQL 8.0 于 2018 年 10 月推出第一版,截止到 2023 年 9 月份,支持到兼容 MySQL8.0.34。除兼容 MySQL 8.0 新功能外,Aurora RDS for MySQL 8.0 也有很多架构选型上的改进以在不同角度提升性能,本节会着重展开介绍。

3.1 多可用区集群 Multi-AZ Cluster

在 RDS MySQL 8.0 推出多可用区集群以前,RDS 有两种部署方式:单可用区实例和多可用区实例。在生产环境中尤其是要求数据库高可用的环境中,我们推荐用户使用多可用区实例。多可用区实例拓扑图如下所示:

在多可用区实例拓扑图中,主可用区有一个 MySQL 数据库实例,在另一个可用区有一个备节点,但该节点并不能对外提供读取服务。为了保证主实例故障时备实例能够有全部数据,主备之间采用同步复制。而 RDS MySQL 由 EC2 上的 MySQL 实例和底层的 EBS 盘构成,这个同步复制是指 EBS 盘之间的数据复制。在主实例故障时,RDS MySQL 会使用第二个可用区中 EBS 盘的数据并快速拉起一个可以对外提供服务的实例,从而快速恢复业务访问。

多可用区实例的架构可以保证主实例故障时快速切换到备实例,并保证业务数据不丢失。但是在平时的使用中,备节点是不能被外部请求访问的。另外对 EBS 盘的强依赖使得多可用区实例对 EBS 的抖动比较敏感,如果任一可用区的 EBS 盘发生故障,都会影响整个数据库的响应时间。

RDS MySQL 8.0 支持的多可用区集群则在多可用区实例的基础上进行了进一步的优化,拓扑结构如下图所示。

多可用区集群由跨三个可用区的三个实例构成,包含一个写节点和两个读节点,写节点和读节点之间通过半同步复制,两个读节点都能对外提供读取访问。多可用区集群有如下优势:

  1. 提升写入操作性能。因为多可用区集群支持的实例是带 SSD 盘的实例,它会将主备节点之间复制的数据存放在 SSD 盘上,无需落到 EBS 盘上,降低了关键路径延迟,也更进一步减少 EBS 盘抖动的影响。
  2. 加快了 failover 速度。因为读节点本身就已经正常对外提供服务,当写节点发生故障时,可以将可能的复制延迟追平后,直接将现有读节点切换成写节点,省掉拉起 MySQL 实例的时间。多可用区集群的 failover 时间通常可以在 35 秒内完成。
  3. 提升了节点利用率。两个备节点都是热备节点,都能对外提供读取访问。
  4. 优化了复制机制。每次写入只要两个从节点中有一个 ACK 了主库的同步请求即可返回成功给用户,能一定程度上降低网络及 EBS 抖动的影响。

关于 Multi-AZ Cluster 的详细介绍,可以进一步参阅官方文档博客

3.2 写入增强 Write-Optimized

RDS MySQL 8.0 提出了写入增强的功能。下图对比展示了没有写入优化和有写入优化的架构图。

在 MySQL 中,默认数据页大小是 16KiB,而 MySQL 程序通常是运行在 Linux 操作系统上的,需要和操作系统交互。Linux 文件系统页(OS Page)的大小默认是 4KiB,所以 MySQL 中一页数据刷到磁盘,要写 4 个文件系统里的页。刷数据页本身这个操作并非原子操作,比如操作系统写到第二个页面的时候,Linux 机器断电了,这时候就会出现问题了,造成“页数据损坏”,并且这种“页数据损坏”靠 redo 日志是无法修复的。

为了防止数据页在刷盘过程中出现问题,MySQL 有 Double Write Buffer。Double Write Buffer 是内存+磁盘的混合架构。当有数据页需要落盘时,InnoDB 会先将要刷盘的页面写到 Double Write Buffer 的内存结构中,然后将 Double Write Buffer 内存里的数据页顺序写出到 Double Write Buffer 对应的磁盘表空间中,最后将 Double Write Buffer 内存里的数据页离散写出到这些数据页对应的真正表空间存储中。如果在将一个 MySQL 数据页写出的过程中操作系统发生了崩溃,MySQL 进行故障恢复时会判断 Double Write Buffer 的磁盘存储中是否有该数据页的完整版本,如果有,会将其直接拷贝到真正表空间存储中,再应用 redo log 进行恢复。这种机制能够防止数据库所在机器故障时数据页有部分新数据部分旧数据从而“坏掉”的情况,但也带来了额外的数据传输损耗和性能损耗。

RDS MySQL 写入优化在某些特定 EC2 机型下,能够利用 Nitro 卡原子性拷贝 16KiB 数据的特性,省掉 Double Write 的步骤,直接一次性将数据拷贝到存储层。写入优化能够将 MySQL 性能提升至 2 倍左右。

关于写入增强的详细介绍,可以进一步参阅官方文档博客

3.3 读取增强 Read-Optimized

RDS MySQL 8.0.28 提供了读取增强的能力。下图对比展示了没有读取优化和有读取优化的架构图。

在 MySQL 中如果运行较大查询,比如聚集、join 等需要存储中间结果的存储等,如果内存空间不够,会使用临时表。在 RDS MySQL 8.0 之前,临时表同普通表一样都是存放在 EBS 盘上的。RDS MySQL 8.0.28 通过带 SSD 的 EC2 实例,将临时表存放在本地的 SSD 盘上。SSD 的磁盘读取能力自然是比 EBS 读取优化很多的,所以 RDS MySQL 8.0 读取优化能够带来性能的提升。

关于读取增强的详细介绍,可以进一步参阅官方文档博客

4. Amazon Aurora for MySQL 8.0 新功能

如果您在升级过程中,也考虑同时切换数据库引擎到 Aurora,或者是未来 Aurora 需要升级版本时,需要了解 Aurora for MySQL 8.0 时,也可以参照本部分内容。Amazon Aurora for MySQL 8.0 也有多个功能增强。下面进行详细展开。

4.1 Aurora Serverless V2

Aurora Serverless 是 Amazon Aurora 的按需自动扩展配置。Aurora Serverless V2 在几分之一秒内将数据库工作负载扩展到数十万个事务。它以细粒度的增量调整容量,为应用程序的需求提供适量的数据库资源。您无需管理数据库容量,只需为应用程序消耗的资源付费。

早在 2018 年 Amazon Aurora 即提供了 Serverless 选项。Amazon Aurora 最新提供的 Aurora Serverless V2 版本相比于上一代 V1 版本更上一层楼,重点提升部分:

  1. 资源容量采用原地扩展,使资源容量扩展速度由 V1 的分钟级提升到秒级。
  2. Aurora Serverless v2 版本能够在容量调整时做到更细粒度,以 0.5 ACU 作为扩展单元(V1 需要翻倍扩展)。
  3. Aurora Serverless V2 能够依据多个维度进行容量调整,包括 CPU、内存、网络等。
  4. Aurora Serverless v2 相比 V1 增加了完整的 Amazon Aurora 功能,包括多可用区支持、只读副本和全球数据库等,支持跨可用区和跨区域的高可用部署和读取扩展。

下图展示了 Aurora Serverless V2 测试的一个结果。橙色线是 Aurora Serverless V2 的 ACU 容量,蓝色线是应用的负载情况。我们可以看到,Aurora Serverless V2 的扩展是和应用程序紧耦合的,可以随着应用程序的变化快速调整资源。

关于 Aurora Serverless V2 的详细介绍,可以进一步参阅官方文档英文博客中文博客

4.2 Aurora Enhanced Binlog

Aurora 本身的读写节点之间通过 Redo log 的复制,通常情况下复制延迟只有 20ms 左右。如果单纯使用 Aurora 本身,是无需开启 Binlog 的。但是,用户有时会有开启 Binlog 的需求,比如需要将 OLTP 的数据持续导出到 OLAP 数据仓库中。MySQL 本身开启 Binlog 会带来对前端应用程序的影响,所以 Aurora 在 Binlog 方面不停进行优化,包括 Aurora 2 的 Yield 机制、binlog IO Cache 等。

Aurora 3.03 支持的 Enhanced Binlog 从另一个角度降低了开启 Binlog 带来的性能损耗。它能将打开 binlog 对应用程序的影响降低到 13% (之前可能最高达到 50%), 也能提升计算节点的吞吐。此外,Binlog 开启时,数据库故障恢复效率与社区版 binlog 相比提升了 99%。

上图是 Aurora 开启 Enhanced Binlog 之前和之后对应的处理逻辑的对比。最大的区别是 Aurora Enhanced Binlog 采用单独存储引擎存放 Binlog,这样刷 Redo Log 和 Binlog 的操作可以同步进行,在事务提交时,直接就通知两个存储引擎进行单独的 Commit 操作,避免了传统的两阶段提交(Redo Log Prepare -> Binlog Commit -> Redo Log Commit)过程中等待 Binlog 文件落盘的过多时间消耗。

单独的 Binlog 存储引擎有几个优势:

1)提升性能:存储层进行 Binlog 排序逻辑,可以增加计算层的并行度,减少加锁,加速事务两阶段提交的速度。

2)加速故障恢复:避免传统 Binlog 故障恢复时必须顺序读取 Binlog 到计算层的操作。可以在保证一致性前提下按需恢复事务。可以将故障恢复时间从几分钟降低到几秒。

3)提供了直接从 Aurora 存储层取 Binlog 的可能性,比如 Aurora 和 Redshift 之间的 ZeroETL 功能就是基于 Enhanced Binlog 来执行实现的,避免了有 Binlog Consumer 连接到 Aurora 计算实例对于计算节点资源的进一步竞争和消耗。

关于 Aurora Enhanced Binlog 的详细介绍,可以进一步参阅官方文档博客

4.3 Aurora IO 优化

Aurora 的 Standard 标准计费模式主要包含三个部分:计算层实例的收费、存储层的收费、以及计算层和存储层之间的 IO 计费。计算层依据实例类型收费,存储层依据数据量大小收费,IO 计费由负载决定。

Aurora 3.03 推出了 IO 优化(IO Optimized)的计费模式,即不再对 IO 计费。所以计费主要包括两大部分:计算层实例收费和存储层收费。与标准计费模式相比,计算层实例计费会额外增加 30%,存储层计费会额外增加 125%。

IO 优化计费模式有如下优点:

  1. IO 优化计费模式下,您只需关注计算和存储计费,无需关注 IO 计费,这样能够有更加确定的费用评估和消耗。
  2. 如果您现在的 Aurora 集群 IO 负载较高,比如 IO 费用占比超过 25%,使用 IO 优化计费模式能帮助您节省 40%的成本。
  3. 支持 Aurora MySQL 和 PostgreSQL,而且支持 Aurora Serverless V2,On-Demand 类型以及预留实例。
  4. 因为预留实例是基于计算层的,IO 部分没有预留收费,将没有预留优惠的 IO 计费转化成有预留优惠的计算层实例,您能够得到更多成本节约。

您可以在创建 Aurora 集群时指定采用 Aurora 标准计费模式或者 IO 优化模式,也可以对现有数据库进行更改。对现有数据库的更改是在线完成的,无需重启数据库。每 30 天内可以做一次从标准计费模式向 IO 优化模式的更改,从 IO 优化模式向标准模式更改没有限制。

关于 Aurora IO 优化的详细介绍,可以进一步参阅官方文档博客

4.4 Aurora 本地写转发

Aurora 的计算层有两种角色,写节点和读节点。写节点可以接收读写的流量,读节点可以接收读取的流量。Aurora 3.04 提出了读节点的本地写转发功能。本地写转发示意图如下,在开启情况下,您可以将写请求发送给读节点,读节点会自动帮您把写请求转发给写节点。因为一个区域内可以配置多达 15 个读节点,而且读节点还有自动伸缩的能力,本地写转发能够提供更好的写流量的扩展。

本地写转发有一个关键参数 aurora_replica_read_consistency,用来指定写后读的一致性。详细了解这个参数之前,我们需要先了解下 Aurora 读写节点的同步逻辑:

  1. Aurora 写节点在进行写操作时,会把 Redo Log 发送给存储层,并采用 6 写 4 成功的 Quorum 协议。
  2. 与此同时,写节点也会把 Redo Log 发送给读节点,读节点会判断自己缓存的数据页里是否有 Redo Log 对应的数据页,如果有,会在内存中将 Redo Log 的修改应用到相应数据页中。
  3. 写节点在写数据到存储层以后,会依赖存储层的返回信息计算一个指标 VDL(Volume Durable LSN),在 VDL 以下的所有 Redo Log 记录都已成功落盘。写节点在向读节点发送 Redo Log 信息时,也会将当时的 VDL 信息发送给读节点。
  4. 读节点会依赖于接收到的 VDL 判断是否它所接收到的 Redo Log 是否已经成功落到存储层,来决定是否应该将 Redo Log 的修改应用到数据页,还是稍作等待。
  5. 读节点接收到的 VDL 一般会早于 Redo Log,所以复制延迟就是一条 Redo Log 在存储层已经持久化的时间和该 Redo Log 传输到读节点被处理的时间之差,Aurora 的复制延迟通常在 20 毫秒左右。

Aurora_replica_read_consistency 有 3 个不同取值:

1) Eventual。表示最终一致性,和不开启本地写转发时读节点的默认一致性相同。读节点看到的数据通常要滞后一段时间,通常情况下读节点和写节点的复制延迟在 20 毫秒左右。 比较适合对读取一致性不敏感的场景,比如每分钟监测一次天气数据的天气监测系统。

2) SESSION。在同一个 SESSION 会话里,后续读请求会等待该 SESSION 之前发送的写请求已经由写节点成功落盘到存储层以后才返回。比较适合只关注自己写入的数据,不关心他人写入数据,或者压根没有他人同时在写数据的场景,比如用户在更改完自己 Profile 个人信息后希望立刻读到自己所做的更改。

3) GLOBAL。在读节点开启事务的第一个读取操作后,它会联系写节点得到当前最新的 VDL,并等待在该 VDL 之前的所有 Redo Log 都传输到该节点再进行处理操作,所以 GLOBAL 方式对应的延迟包括:读写节点之间 Round-Trip 交互 VDL 的时间和读写节点之间的数据延迟。比较适合对读取延迟不敏感,希望简单一个连接能够同时处理读写的场景,比如银行交易系统可能存在一张表同时操作的场景,大家都希望所有读写操作都在最新数据上进行。如果您希望既能保证强一致性,又能有较低的读取延迟,这种情况仍然可以把读请求直接发送到写节点。

关于 Aurora 本地写转发的详细介绍,可以进一步参阅官方文档英文博客中文博客

5. 总结

本篇博客重点介绍了 MySQL 8.0 的一些新功能,包括开源 MySQL 8.0 的功能和性能提升,比如 Online DDL、通用表达式 CTE、窗口函数 Window Function、降序索引和不可见索引;AWS RDS for MySQL 8.0 在架构上的改进以提升性能,包括 Multi-AZ Cluster,写入优化和读取优化;Amazon Aurora for MySQL 8.0 的功能、性能增强和成本优化,包括 Serverless V2、Enhanced Binlog、IO 优化以及读节点的本地写转发。总体而言,不管是开源还是自研的产品,新的功能和改进通常都会基于最新的版本。如果可能的话,我们建议您考虑将 MySQL 积极升级到 8.0 版本,并在升级之前针对新功能和应用的适配性做好充分的测试。

系列博客

本篇作者

马丽丽

亚马逊云科技数据库解决方案架构师,十余年数据库行业经验,先后涉猎 NoSQL 数据库 Hadoop/Hive、企业级数据库 DB2、分布式数仓 Greenplum/Apache HAWQ 以及亚马逊云原生数据库的开发和研究。

李芬芳

亚马逊云科技数据库专家,负责亚马逊云科技数据库相关的架构优化、成本管理、技术咨询等工作。加入 AWS 之前曾就职于腾讯、唯品会、圆通速递等公司,有多年数据库管理经验。