亚马逊AWS官方博客

丝滑柔顺:Amazon RDS 多可用区集群结合 Amazon RDS Proxy 实现小版本升级秒级切换

数据库生命周期是不断变化的,随着时间推移,旧的版本会逐渐停止支持。升级数据库版本成为需要考虑的因素。云上托管数据库降低了客户的管理成本,并且增加了很多新功能以满足业务需求。例如,高可用架构,在出现故障、维护、修改实例类型、版本升级时,能利用备用实例降低停机时间。但是,由于 DNS 缓存机制的存在,主节点 IP 变化后,应用程序 DNS 缓存仍然是失效的 IP,需要等待一点时间 IP 更新后,才能重新成功连接数据库,此期间应用不可访问数据库。对于停机时间敏感的业务,例如金融支付,1 分钟的停机时间仍然过长。客户需要寻找停机时间更短的数据库方案。

Amazon RDS 支持两种多可用区高可用方案:多可用区实例多可用区集群。故障恢复时间分别可以达到 1 分钟和 35 秒。如果需要更短的故障恢复时间,多可用区集群结合 Amazon RDS Proxy,可以达到 1 秒停机时间

方案架构如下:

在此方案中,数据库选择 Amazon RDS 多可用区集群,在应用程序和数据库之间增加代理。通过代理,应用程序可以池化和共享数据库连接,实现连接复用,减少应用程序每次与数据库建立和关闭连接的资源消耗,以提高其扩展能力。RDS 代理还可以将多可用区数据库集群的小版本升级的停机时间缩短到一秒或更短。

小版本版本升级时,Amazon RDS 首先一次升级一个只读实例。然后,其中一个只读数据库实例将切换为新的写入实例。然后,Amazon RDS 升级旧的写入实例(现在是只读实例)。

此过程中,RDS 代理不仅可以通过连接池复用连接,还能获取升级过程中的主实例和只读实例的角色变化,而不是完全依赖 DNS 更新 IP,从而减少整体停机时间。

以下对于不同的场景,分别测试应用访问数据库不可用时间。

测试环境

数据库:Amazon RDS Mysql 8.0.34(后续升级到 8.0.37),多可用区集群,实例类型:m6id.large,磁盘:100GB GP3, 3000 IOPS

客户端:EC2, Amazon Linux 2023, m6i.large

创建 RDS Proxy,指向 RDS,对应用暴露两个域名:读写和只读。本测试中使用读写域名。

在 RDS Mysql 创建 2 个表:

time_table:测试程序每 0.1 秒写入时间戳
dummy_table:模拟业务流量,批量写入数据

CREATE TABLE `dummy_table` (
  `id` int NOT NULL AUTO_INCREMENT,
  `column1` varchar(10) DEFAULT NULL,
  `column2` int DEFAULT NULL,
  `column3` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

CREATE TABLE `time_table` (
  `id` int NOT NULL AUTO_INCREMENT,
  `ts` datetime(3) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

Python 测试程序 1 rdstaz-proxy.py,记录应用连接数据库情况。连接 RDS proxy,每 0.1 秒写入一条带时间戳的数据。如果出现连接中断等异常,会每隔 1 秒重新连接,直到连接成功。

测试程序 2 rds-insert-proxy.py,模拟数据库大量写入。连接 RDS proxy,多线程批量插入大量数据,实测 4 线程,batch size 1000。

测试过程

运行测试程序 1,观察每个场景下的中断时间。

测试 1:数据库重启,并且启用 RDS proxy: 数据库本身重启,不触发故障切换,实例 IP 和角色也不会变化,时间持续较长。

以下测试程序输出,异常为数据库不可连接的时间,持续 75 秒。

ID: 238220, Time: 2024-07-01 13:45:54.807000
ID: 238221, Time: 2024-07-01 13:45:54.915000
ID: 238222, Time: 2024-07-01 13:45:55.023000
Error executing SQL statements: (1053, 'Server shutdown in progress')
Reconnecting to the database...
ID: 238223, Time: 2024-07-01 13:46:10.220000
ID: 238224, Time: 2024-07-01 13:47:00.322000
ID: 238225, Time: 2024-07-01 13:47:00.432000

测试 2: 数据库故障切换,无 RDS proxy。应用直接连接数据库写端点,相对 RDS 多可用区实例方式,数据库不可用时间,从 1-2 分钟降低到通常 35 秒以内,已经大幅降低。

以下测试结果显示,数据库不可用时间为 23 秒。

ID: 22236, Time: 2024-07-01 02:41:29.493000
ID: 22237, Time: 2024-07-01 02:41:29.606000
Error executing SQL statements: (2013, 'Lost connection to MySQL server during query')
Reconnecting to the database...
Error connecting to MySQL database: (2003, "Can't connect to MySQL server on 'taztest.cluster-c7b8fns5un9o.us-east-1.rds.amazonaws.com' ([Errno 111] Connection refused)")

mazonaws.com' ([Errno 111] Connection refused)")
Error connecting to MySQL database: (2003, "Can't connect to MySQL server on 'taztest.cluster-c7b8fns5un9o.us-east-1.rds.amazonaws.com' (timed out)")
ID: 22239, Time: 2024-07-01 02:41:52.995000
ID: 22240, Time: 2024-07-01 02:41:53.098000

测试 3:数据库故障切换,有 RDS proxy,无请求压力。Proxy 可以更快感知主从切换时的角色变化,而不依赖于 DNS,切换时间更快。故障切换包含检测流程,还是需要一些时间。

数据库不可用时间:11 秒

ID: 236261, Time: 2024-07-01 13:42:11.881000
ID: 236262, Time: 2024-07-01 13:42:11.987000
Error executing SQL statements: (1053, 'Server shutdown in progress')
Reconnecting to the database...
ID: 236263, Time: 2024-07-01 13:42:22.698000
ID: 236264, Time: 2024-07-01 13:42:22.806000

测试 4:数据库故障切换,有 RDS proxy,运行程序 2进行加压。当有大量数据库访问压力时(此测试中每秒写入 90000 条数据),主从延迟加大。在 RDS 多可用区集群架构中,故障切换要等待两个 reader 都同步数据后再切换,故障切换时间受主从延迟影响,从而延长整个过程。

数据库不可用时间:97 秒

ID: 41539, Time: 2024-07-01 05:28:56.452000
Error executing SQL statements: (1053, 'Server shutdown in progress')
Reconnecting to the database...
ID: 41540, Time: 2024-07-01 05:28:56.702000
ID: 41541, Time: 2024-07-01 05:30:33.052000

观察 RDS 指标,主从延迟在 5:30 左右,增加到约 100 秒。这也增加了整个数据库的恢复时间。

测试 5:更改 RDS 实例类型,有 RDS proxy。此过程中,备用实例修改完成后切换,而 proxy 可以主动感知切换动作,无需依赖 DNS 更新,达到秒级切换效果。

数据库不可用时间:不到 1 秒

ID: 156217, Time: 2024-07-01 11:13:17.849000
ID: 156218, Time: 2024-07-01 11:13:17.956000
Error executing SQL statements: (2013, 'Lost connection to MySQL server during query')
Reconnecting to the database...
ID: 156219, Time: 2024-07-01 11:13:18.595000
ID: 156220, Time: 2024-07-01 11:13:18.705000

测试 6:更改 RDS 实例类型,有 RDS proxy,同时有大量请求,本测试中每秒写入 100000 条数据。即使在高压力的情况下,仍然能保持很短的切换时间。

数据库不可用时间:1.3 秒

ID: 260786, Time: 2024-07-01 14:29:26.219000
ID: 260787, Time: 2024-07-01 14:29:26.330000
ID: 260788, Time: 2024-07-01 14:29:26.438000
Error executing SQL statements: (2013, 'Lost connection to MySQL server during query')
Reconnecting to the database...
ID: 260789, Time: 2024-07-01 14:29:27.787000
ID: 260790, Time: 2024-07-01 14:29:27.900000
ID: 260791, Time: 2024-07-01 14:29:28.010000
ID: 260792, Time: 2024-07-01 14:29:28.119000

测试 7:升级数据库小版本,有 RDS proxy 。升级时数据库会断开连接,顺序是先升级 2 个只读实例,然后切换主从角色,此过程会有短暂不可用时间,一般在 1 秒以内。

数据库不可用时间:不到 1 秒

ID: 230683, Time: 2024-07-01 13:31:25.187000
ID: 230684, Time: 2024-07-01 13:31:25.294000
ID: 230685, Time: 2024-07-01 13:31:25.402000
Error executing SQL statements: (2013, 'Lost connection to MySQL server during query')
Reconnecting to the database...
ID: 230686, Time: 2024-07-01 13:31:26.084000
ID: 230687, Time: 2024-07-01 13:31:26.191000
ID: 230688, Time: 2024-07-01 13:31:26.310000

数据库小版本升级完成

测试 8:升级数据库小版本,有 RDS proxy,加上一些请求量。此测试模拟请求量不大的时候,数据库版本升级。

数据库不可用时间:1 秒左右

以上测试程序直接使用 python mysql 库,没有使用应用连接池。虽然可以通过重试,重新连接数据库,但是应用程序需要捕获连接异常,并且新建连接。如果需要更丝滑的应用体验,可以考虑更改应用连接池方式,结合 proxy,实现应用无需断开连接,即可更改实例类型或者升级。应用会从连接池里获取连接,整个过程没有异常抛出。

测试程序 3: rdstaz-proxy-pool.py

from DBUtils.PooledDB import PooledDB

使用 PooledDB 库,实现应用连接池化。

测试 9:RDS proxy,应用连接池,数据库重启。

应用不能写入,连接未断开,46 秒后继续写入。

ID: 1752, Time: 2024-07-01 16:17:14.495000
ID: 1753, Time: 2024-07-01 16:17:14.607000
ID: 1754, Time: 2024-07-01 16:18:00.664000
ID: 1755, Time: 2024-07-01 16:18:00.978000

测试 10:RDS proxy,应用连接池,故障切换。

应用不能写入,连接未断开,12 秒后继续写入。

ID: 4200, Time: 2024-07-01 16:22:36.334000
ID: 4201, Time: 2024-07-01 16:22:36.447000
ID: 4202, Time: 2024-07-01 16:22:36.557000
ID: 4203, Time: 2024-07-01 16:22:36.825000
ID: 4204, Time: 2024-07-01 16:22:48.222000
ID: 4205, Time: 2024-07-01 16:22:48.332000
ID: 4206, Time: 2024-07-01 16:22:48.442000

测试 11:RDS proxy,应用连接池,更改 RDS 实例类型。

应用未中断,未发现写入有超过一秒的延迟。

测试 12:RDS proxy,应用连接池,升级数据库小版本。

直到升级完成,应用未感觉到中断。检查时间戳记录表,在升级切换的时间段范围内,每秒都有几行数据写入,说明无写入或者中断时间在 1 秒内。

总结

Amazon RDS 多可用区集群,结合 Amazon RDS proxy,各场景下应用访问数据库不可用时间(秒)

结论

  • Amazon RDS 多可用区集群,结合Amazon RDS Proxy,在更改实例类型和数据库小版本升级的场景下,可以把数据库不可用时间控制在 1 秒。
  • 故障切换由于需要故障检测,并且等待主备延迟追平,时间相对较长。
  • 如果应用程序需要保持连接不中断,而不是重新连接,可以使用应用连接池,加上 Amazon RDS proxy,实现无中断升级版本或修改实例类型。

Amazon RDS 多可用区集群加上 Amazon RDS proxy,可以实现版本升级和修改实例类型时的秒级切换,以满足金融支付等需要 7*24 在线的苛刻场景。此外,也提升了数据库读写性能,并且降低了磁盘延迟增加时可能造成的数据库性能影响。请参考 Amazon RDS 多可用区部署选项比较及选型建议

本篇作者

章平

亚马逊云科技数据库架构师。2014 年起就职于亚马逊云科技,先后加入技术支持和解决方案团队,致力于客户业务在云上高效落地。对于各类云计算产品和技术,特别是在数据库和大数据方面,拥有丰富的技术实践和行业解决方案经验。此前曾就职于 Sun,Oracle,Intel 等 IT 企业。