{"value":"### **1. 前言**\n数据库,尤其是 OLTP 的数据库是应用程序的基石。尽管成熟的数据库通常运行起来都相对稳定,但是否能容忍更多的底层故障,是否能快速地从故障中恢复出来,对应用程序而言是非常重要的。Amazon Aurora 作为亚马逊自研的云原生数据库,除兼容性、性能、扩展性外,它在设计之初,就以极致的可用性作为目标,尽可能减少故障对应用程序的影响。\n\nAmazon Aurora 在故障恢复方面的设计理念主要包括:\n\n**1.能在较大范围故障时仍然提供服务**:跨3个可用区的6备份存储使它在一个可用区和另一个额外备份发生故障时仍能提供服务;跨区域的全球数据库能在主区域发生故障时快速切换到从区域;\n\n**2.加快故障恢复的速度**:将数据拆分成10 GB 粒度存储单元使单个存储单元能在秒级别恢复;通过分布式存储进行并行恢复等;快速找到健康计算节点先进行节点替换来使整个集群尽快提供服务。\n\n![44ec984ec84de1242543609f16ac00b7.png](https://dev-media.amazoncloud.cn/a3096a44364b4ed3933d3bc3296c37ae_44ec984ec84de1242543609f16ac00b7.png)\n\nAmazon Aurora 的架构示意图如上所示,它以端点 endpoint 的形式对外提供服务,用户可以通过读写端点来访问 Aurora 写节点;通过只读端点访问 Aurora 读节点。当写节点发生故障时,Aurora 会进行如下的 failover 过程:\n\n1. 根据不同节点 failover 优先级和复制延迟来选择一个读节点,将其提升成新的写节点。因为该节点角色发生切换,需要重启该节点。\n2. 尝试恢复原来的写节点并让它成为新的读节点。这里也会涉及到节点重启。\n3. 待新的写节点重启成功以后,定位读写端点指向新的写节点。这里涉及到域名的更新,依赖于 Route53 的实现。\n4. 待新的读节点重启成功以后,定位只读端点指向新的读节点。这里同样涉及到域名更新,依赖于 Route53 的实现。\n\n通常情况下,Aurora 的 failover 通常能在30-60秒内完成,能对应用程序产生较低影响。那么,是否有可能进一步加快 Aurora failover 的速度呢?本篇文章会重点介绍如何在 Aurora 进行故障恢复的时候避免或者减少 DNS 的影响(步骤3),从而加快故障切换速度。具体内容涵盖故障切换各步骤的时间消耗、花费时间的分析排查以及减少对应用程序影响的几种方案。\n\n### **2. Aurora failover 的过程**\n在 Aurora 发生 failover 的时候,我们可以通过观测 Event 事件来查看 failover 过程中 Aurora 的读写节点究竟发生了哪些操作。以一写一读的集群为例,如果我们在控制台点击 failover,可以观测到如下事件。\n\n![5a273f20d6022a86431380ed84daf6bb.png](https://dev-media.amazoncloud.cn/3e2e2909abb74bf9b26ec0ffa6abe8f6_5a273f20d6022a86431380ed84daf6bb.png)\n\n其中,aurora-2092-binlog-on 为 Aurora 集群,aurora-2092-binlog-on-instance-1 为原来的读节点,aurora-2092-binlog-on-instance-1-us-east-1b 为原来的写节点。\n\n从事件中我们可以看到,当 Aurora 集群发生故障切换时,会先后有如下事件生成:\n\n1. 集群开始 failover\n2. 原来读节点重启(会成为新的写节点)\n3. 原来写节点重启(会成为新的读节点)\n4. 新写节点重启成功\n5. 新读节点重启成功\n6. 集群 failover 完成\n\n\n整个集群 failover 过程在29秒完成。其中,新的写节点重启成功用了6秒,也就是新的写节点在6秒以后就可以接受请求。但是,由于用户访问 Aurora 是利用域名的方式(Aurora 的读写域名和只读域名),新写节点启动后需要更新域名,而域名更新需要一定的时间,所以应用程序用域名连接 Aurora 时体验到的连接中断时间会高于6秒。\n\n![0ffb9706a80f5300f6389deab0610e98.png](https://dev-media.amazoncloud.cn/0565dc6ca2e24f35bfdd011bbcffe788_0ffb9706a80f5300f6389deab0610e98.png)\n\n### **3. 如何分析故障切换时,时间消耗在哪里?**\n当遇到 Aurora 集群故障切换影响应用程序较长时间时,我们可以采用如下几步甄别最长的时间花费在哪里,再对症下药,进行优化。\n#### **3.1 信息收集**\n除了第2节讲的对 event 的观测以外,我们可以在访问 Aurora 集群的客户端环境中打印 DNS 对应 IP 的信息,来获得 DNS 域名切换的时间。\n\n可以采用如下代码:\n```\ncat nslookup.sh \n#!/bin/bash\nwhile true\ndo\nrwendpoint=$(nslookup aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com)\nroendpoint=$(nslookup aurora-2092-binlog-on.cluster-ro-cttuqplcao81.us-east-1.rds.amazonaws.com)\nnow=$(date +\"%Y-%m-%d %H:%M:%S\")\necho \"$now \\n $ rwendpoint \\n $ roendpoint \"\necho \"----------------------\"\nsleep 1\ndone\n```\n另外,Aurora 的写节点和读节点的信息可以从这张系统表中 information_schema.replica_host_status 推理得到,也可以设计类似于监控域名变化的逻辑实时监控读写节点的状态。这张表里读写节点的变化是不依赖于 DNS 的,应该与 Event 中的节点重启的时间能够匹配。\n```\nMySQL [(none)]> select server_id AS Instance_Identifier, if(session_id = 'MASTER_SESSION_ID','writer', 'reader') as Role from information_schema.replica_host_status;\n+---------------------------------------------+--------+\n| Instance_Identifier | Role |\n+---------------------------------------------+--------+\n| aurora-2092-binlog-on-instance-1 | writer |\n| aurora-2092-binlog-on-instance-1-us-east-1b | reader |\n+---------------------------------------------+--------+\n2 rows in set (0.00 sec)\n```\n#### **3.2 分析排查**\n\nAurora 的 event 发生的时间是分析排查的基础。总体来讲,进行分析排查可以分两步走:\n\n首先判断是否 Aurora 新写节点启动本身消耗时间比较久。对比集群开始 failover 的时间和新写节点重启完成的时间。如果此部分花费时间过长,有可能是在发生故障时应用的并发负载比较大,所以新的写节点启动之前需要处理发生故障时正在执行的事务的信息,比如 redolog, undolog 以及 binlog 的处理和恢复等。对于这种情况,可以考虑进行应用负载的优化或者是查看 Aurora 新的版本是否有相应的优化,考虑升级。例如,Aurora 的版本1.23以上版本及2.08以上版本对于大的事务(单个事务对应的 binlog 文件大于0.5 GB)恢复速度有明显提升,如果您的 Aurora 集群中打开了 binlog 并且有大事务,可以考虑升级 Aurora 版本。或者如果 binlog 不是必须的,可以直接通过参数修改把 binlog 关闭。\n\n接下来判断故障恢复时间是否由 DNS 更新延迟引发。可以结合 Aurora event 打印时间和 nslookup.sh 的输出对比进行分析。以第2节的 failover 的事件发生时为例,nslookup.sh 的脚本输出为:\n```\n2022-04-22 09:37:11 \\n Server: 172.31.0.2\nAddress: 172.31.0.2#53\n\nNon-authoritative answer:\naurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com canonical name = aurora-2092-binlog-on-instance-1-us-east-1b.cttuqplcao81.us-east-1.rds.amazonaws.com.\nName: aurora-2092-binlog-on-instance-1-us-east-1b.cttuqplcao81.us-east-1.rds.amazonaws.com\nAddress: 172.31.11.251 \\n Server: 172.31.0.2\nAddress: 172.31.0.2#53\n\nNon-authoritative answer:\naurora-2092-binlog-on.cluster-ro-cttuqplcao81.us-east-1.rds.amazonaws.com canonical name = aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com.\nName: aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com\nAddress: 172.31.12.155\n\n----------------------\n2022-04-22 09:37:12 \\n Server: 172.31.0.2\nAddress: 172.31.0.2#53\n\nNon-authoritative answer:\naurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com canonical name = aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com.\nName: aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com\nAddress: 172.31.12.155 \\n Server: 172.31.0.2\nAddress: 172.31.0.2#53\n\nNon-authoritative answer:\naurora-2092-binlog-on.cluster-ro-cttuqplcao81.us-east-1.rds.amazonaws.com canonical name = aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com.\nName: aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com\nAddress: 172.31.12.155\n```\n我们可以看到,在9:37:12的时候发生的读写 DNS 域名的切换,读写 DNS 均指向了新的写节点,在此之前,读写 DNS 指向的还是原来的写节点。事实上,从第2节我们可以看到,新的写节点在9:37:03的时候就已经重启成功了,所以对于应用程序来说,这9秒的时间是在等待 DNS 的更新。\n\n### **4. 减少 DNS 切换的影响,提升 Failover 的速度**\n如果经过第3节的分析发现 DNS 的切换速度比较慢,也就是写节点重启成功以后 DNS 还需要经过一段时间才能更换,我们可以采用下面几种方式进行改进。\n\n#### **4.1 控制客户端 DNS TTL 的时间**\n检查客户端是否缓存了 DNS 及设置时间。如果设置较久的话,可以考虑将客户端的 DNS 的 TTL 调小。如果您的客户端应用程序正在缓存数据库实例的域名服务 (DNS) 数据,请将生存时间 (TTL) 值设置为小于 30 秒。数据库实例的底层 IP 地址在故障转移后可能会发生变化。因此,如果您的应用程序试图连接到不再使用的 IP 地址,那么缓存 DNS 数据达较长时间可能导致连接失败。如果连接使用读取器终端节点,并且只读副本实例之一处于维护状态或已删除,那么具有多个只读副本的 Aurora 数据库集群也会遭遇连接失败。\n#### **4.2 使用 RDS Proxy**\nRDS Proxy 是基于 Aurora/RDS 之上提供的一个代理层,它有三个特性:\n\n1)连接池能够实现连接的多路复用。如果应用对数据库的并发请求比较多,直接打到 Aurora 数据库上会耗费很多资源。可以使用 RDS Proxy 来实现连接复用,支持更多的并发应用,并减少新建连接的需要。\n\n2)增强的安全性。如果不希望直接把底层数据库的密码直接暴露,可以使用 RDS Proxy,通过使 RDS Proxy 访问 Secrets Manager 里的密码来增强安全性。 \n\n3)快速的故障恢复时间。RDS Proxy 避开了 Aurora 发生故障切换时节点切换带来的域名 DNS 记录更新的问题,用户通过连接 RDS Proxy 可以避免域名更新的时间消耗。\n\n在现有的 Aurora 集群上开启 RDS Proxy,您可以参考此篇文档中的步骤来实现。应用程序端所做的更改是将 Aurora读写的端点替换成 RDS Proxy 的端点,因为 RDS Proxy 也提供对 Aurora 只读端点的访问和封装。如果您的应用程序中使用了 Aurora 的只读端点,也是可以使用 RDS Proxy 的读取端点来进行配置的。\n\n为测试 RDS Proxy 的效果,可以利用类似下面小程序来对比在Aurora故障恢复期间直接连接 Aurora 集群和连接 RDS Proxy 对应的影响。程序很简单,每隔 1s 连接到 Aurora 或者 RDS Proxy 集群运行一次 update+select now() 的操作。连接到 Aurora 的程序:\n\n文档:\n[https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/AuroraUserGuide/rds-proxy-setup.html](https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/AuroraUserGuide/rds-proxy-setup.html)\n```\ncat testAurora.sh\n#!/bin/bash\nwhile true\ndo \n mysql -h aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com -uadmin -p12345678 -Dlili -e \"update wr_table_aurora set c='new' where a=2; select now();\"\n now=$(date +\"%T\")\n echo \"update done: $now\"\n sleep 1\ndone\n```\n在第2节的故障恢复的恢复过程中,可以看到连接到脚本的执行输出为:\n```\n./testAurora.sh >testAurora.out\n[ec2-user@ip-172-31-1-113 rdsproxy-failover]$ ./testAurora.sh >testAurora.out\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 1290 (HY000) at line 1: The MySQL server is running with the —read-only option so it cannot execute this statement\nERROR 1290 (HY000) at line 1: The MySQL server is running with the —read-only option so it cannot execute this statement\nERROR 1290 (HY000) at line 1: The MySQL server is running with the —read-only option so it cannot execute this statement\nERROR 1290 (HY000) at line 1: The MySQL server is running with the —read-only option so it cannot execute this statement\nERROR 1290 (HY000) at line 1: The MySQL server is running with the —read-only option so it cannot execute this statement\n```\n查看 testAurora.out 的输出片段:\n```\nnow()\n2022-04-22 09:36:57\nupdate done: 09:36:57\nnow()\n2022-04-22 09:36:58\nupdate done: 09:36:58\nupdate done: 09:36:59\nupdate done: 09:37:00\nupdate done: 09:37:01\nupdate done: 09:37:02\nupdate done: 09:37:03\nupdate done: 09:37:04\nupdate done: 09:37:05\nupdate done: 09:37:06\nupdate done: 09:37:08\nupdate done: 09:37:09\nupdate done: 09:37:10\nupdate done: 09:37:11\nupdate done: 09:37:12\nnow()\n2022-04-22 09:37:13\nupdate done: 09:37:13\nnow()\n2022-04-22 09:37:14\nupdate done: 09:37:14\n```\n从9:36:59一直到9:37:12,应用程序一直是不可用的状态,一开始会报后台数据库连接不上的错误,然后会报只读 read-only 错误,原因在于在原来的写节点重启的过程中,应用程序是不停去连接的,所以报连接失败错误,而当写节点也就是新的读节点重启成功以后,因为域名还在指向它,所以会报只读错误,直至9:37:12域名更新以后应用程序才恢复正常。\n\n使用 RDS Proxy 测试的脚本内容为:\n```\ncat testRDSProxy.sh\n#!/bin/bash\nwhile true\ndo \n mysql -h aurora-proxy-2.proxy-cttuqplcao81.us-east-1.rds.amazonaws.com -uadmin -p12345678 -Dlili -e \"update wr_table set c='new' where a=2; select now();\"\n now=$(date +\"%T\")\n echo \"update done: $now\"\n sleep 1\ndone\n```\n在 failover 的过程中运行脚本:\n```\n./testRDSProxy.sh >testRDSProxy.out\n```\n查看 testRDSProxy.out 的输出片段:\n```\n2022-04-22 09:36:57\nupdate done: 09:36:57\nnow()\n2022-04-22 09:36:58\nupdate done: 09:36:58\nnow()\n2022-04-22 09:37:03\nupdate done: 09:37:03\nnow()\n2022-04-22 09:37:04\nupdate done: 09:37:04\nnow()\n2022-04-22 09:37:05\nupdate done: 09:37:05\nnow()\n```\n应用程序同样在9:36:59开始没有结果返回,但是在9:37:03即恢复正常,这个时间也就是第2节提到的新写节点启动的时间,4秒的 failover 时间相对于直接连接 Aurora 集群的13秒的时间节约了9秒的时间。此外,在使用 RDS Proxy 的过程中,从9:36:59到9:37:03之间的应用并没有报错信息,这是因为 RDS Proxy 会在底层 Aurora 故障切换的过程中对新发送过来的应用程序进行缓存,当新的写节点启动后,直接将缓存的 SQL 语句发送给新的写节点,这样能一定程度上降低应用程序直接出错带来的影响。\n\nRDS Proxy 的实现逻辑是它能够动态连接到底层 Aurora 数据库的各个实例,进而在实例状态发生变化时及时捕捉,并更新内部的路由定向机制,所以成功避免了 DNS 域名切换的时间,提升了 Aurora 故障切换的速度。此外,RDS Proxy 是 Serverless 的架构,开启较为简单,而且可以根据应用程序对数据库的负载弹性伸缩,可以做为您在考虑降低 failover DNS 切换时间的一个选择。\n\n#### **4.3 使用智能驱动**\n如果您的应用程序采用的是 Java 语言,除 RDS Proxy 外,也可以考虑使用亚马逊提供的**智能驱动**。智能驱动在连接到 Aurora 集群时,会拿到整个集群的拓扑和各个节点的角色信息(是写节点还是读节点)维持在缓存中。有了这个拓扑,在节点发生变化时,就可以快速拿到变化的节点的角色信息,而无需再依赖的 DNS 的解析。\n\n智能驱动:\n[https://awslabs.github.io/aws-mysql-jdbc/](https://awslabs.github.io/aws-mysql-jdbc/)\n\n![4caa60065ff892031f7ba7e15e937df1.png](https://dev-media.amazoncloud.cn/724f78690d694759812e84864a1ab1df_4caa60065ff892031f7ba7e15e937df1.png)\n\n上图是智能驱动自身的逻辑示意,应用程序连接到它时,会维护一个逻辑的连接和物理的连接。比如现在的写节点是 C,但同时它的拓扑缓存中会存放着节点 A 和 B 的信息,这样如果节点 C 发生了故障,能够及时检测并将物理连接切换到 A 或 B 节点(取决于 Aurora 决定 failover 到哪个节点)而逻辑连接是保持不变的。\n\n智能驱动与 MySQL 的普通驱动使用方法是一样的,您只需要更换驱动,并将连接字符串更换为如下格式即可。注意这里的 url 的字符串前面是 jdbc:mysql:aws。缺省情况下,故障切换的能力是开启的,也可以通过参数调整的方式进行关闭。\n\njdbc:mysql:aws://* 集群名称*.*集群 id*.us-east-2.rds.amazonaws.com:3306/* 数据库名 *?useSSL=false&characterEncoding=utf-8 \n\n这篇**博客**里测试对比了智能驱动与普通依赖于 DNS 解析的驱动对应用程序影响的时间。和 RDS Proxy 类似,平均影响时间会降至4秒。详细测试方法可以查阅该博客。\n\n博客:\n[https://aws.amazon.com/cn/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/\n](https://aws.amazon.com/cn/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/\n)\n![image.png](https://dev-media.amazoncloud.cn/5ae23480dd364299884b7f80ef0296df_image.png)\n亚马逊的智能驱动是开源的,采用的 Apache 协议。如果您的应用程序不是用 Java 语言,也可以采用类似逻辑来进行实现,即拿到 Aurora 集群的拓扑结构和各个节点的角色信息,然后阶段性连接(比如每隔1秒),看是否发生变化,如果发生变化构建新的物理连接。\n\n#### **4.4 事件捕捉,触发连接重置**\n另外一种可以考虑的思路是利用事件监控机制,触发 Lambda 方法,在 Lambda 方法中检测到新写节点重启成功或者是集群重启成功以后,触发应用程序的重联机制。这篇**博客**的最后一个小节里提供了一个示例来使用 Lambda 方法来触发 ShardingSphere-Proxy 到 Aurora 的数据源创建的 DistSQL 语句,从而避免了只读错误不断抛出的问题。\n\n博客:\n[https://aws.amazon.com/cn/blogs/china/make-further-progress-shardingsphere-proxy-chapter-of-amazon-auroras-reading-and-writing-ability-expansion/\n](https://aws.amazon.com/cn/blogs/china/make-further-progress-shardingsphere-proxy-chapter-of-amazon-auroras-reading-and-writing-ability-expansion/\n)\n如果有些语言的驱动进行了 IP 地址的缓存,在 DNS 切换时并不能检测到,仍然使用原来 IP 进行连接的话,会一直报只读的错误。这种情况,也可以考虑使用 SQL 错误码分析并重置连接的方式来做为一个短期解决问题的方案。以 MySQL 8.0 为例,一般需要关注的信息有:\n\n- 1836错误码。数据库在只读模式。\n- 1792错误码。事务在只读模式。\n- Analyze/optimize 表的输出中含有 read-only 的错误。\n\n下面是抛出错误的一些示例:\n```\nMySQL [(none)]> create database test;\nERROR 1836 (HY000): Running in read-only mode\n\nMySQL [aaa]> begin;\nQuery OK, 0 rows affected (0.00 sec)\nMySQL [aaa]> select count() from wr_table;\n+----------+\n| count() |\n+----------+\n| 1277 |\n+----------+\n1 row in set (0.01 sec)\n\nMySQL [aaa]> insert into wr_table values(51);\nERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.\n\nMySQL [aaa]> analyze table wr_table;\n+--------------+---------+----------+----------------------------------------------------------------+\n| Table | Op | Msg_type | Msg_text |\n+--------------+---------+----------+----------------------------------------------------------------+\n| aaa.wr_table | analyze | Warning | InnoDB: Running in read-only mode |\n| aaa.wr_table | analyze | Error | Running in read-only mode |\n| aaa.wr_table | analyze | Error | Unable to store dynamic table statistics into data dictionary. |\n| aaa.wr_table | analyze | status | Unable to write table statistics to DD tables |\n+--------------+---------+----------+----------------------------------------------------------------+\n4 rows in set (0.00 sec)\n\nMySQL [aaa]> optimize table wr_table;\n+--------------+----------+----------+-----------------------------------+\n| Table | Op | Msg_type | Msg_text |\n+--------------+----------+----------+-----------------------------------+\n| aaa.wr_table | optimize | Warning | InnoDB: Running in read-only mode |\n| aaa.wr_table | optimize | Warning | InnoDB: Running in read-only mode |\n| aaa.wr_table | optimize | Error | Running in read-only mode |\n| aaa.wr_table | optimize | error | Corrupt |\n+--------------+----------+----------+-----------------------------------+\n4 rows in set (0.00 sec)\n```\n下面这张表里总结了几种不同的方案和各自的优缺点,供您在降低 Aurora 故障切换 DNS 切换时间的影响时进行参考。总体而言,如果应用程序是 Java 语言写的,推荐您使用智能驱动;如果是非 Java 语言,推荐您使用 RDS Proxy;如果对性能要求很高,可以在短期内考虑事件触发重联,长期考虑参照亚马逊的智能驱动逻辑自己实现一个依赖于 Aurora 系统表元信息变更而不是域名切换的智能驱动。\n\n![image.png](https://dev-media.amazoncloud.cn/2353a4e2ca664470818f9b02251c5467_image.png)\n\n### **5. 结语**\nAmazon Aurora 独特的计算存储分离的架构、日志即数据库的设计以及其他的一些优化,能够使它在发生主节点故障时进行快速的故障恢复,然而切换后的节点是否能够快速地被应用程序检测到是受 DNS 域名解析的限制的。\n\n本文分析了 Aurora 故障切换时发生的 event 事件,提出了排查的思路,并提出了几种不同的方案来减轻 DNS 解析的影响,比如减少客户端 DNS 缓存 TTL,增加 RDS Proxy 层避免 DNS 的影响,使用智能驱动来绕过 DNS 解析的时间延迟,或者是通过事件通知机制、错误码分析等。您可以在实际应用中,针对不同的需求选择合适的方案来更近一步地利用到 Aurora 的快速故障切换,提升数据库的可用性,降低对应用程序的影响。\n\n### **本篇作者**\n\n\n**马丽丽**\n亚马逊云科技数据库解决方案架构师,十余年数据库行业经验,先后涉猎 NoSQL 数据库 Hadoop/Hive、企业级数据库DB2、分布式数仓Greenplum/Apache HAWQ 以及亚马逊云原生数据库的开发和研究。\n\n[阅读原文](https://aws.amazon.com/cn/blogs/china/be-vigilant-in-times-of-peace-amazon-aurora-fault-recovery-reduces-the-impact-of-dns-switching-on-applications/)","render":"<h3><a id=\"1__0\"></a><strong>1. 前言</strong></h3>\n<p>数据库,尤其是 OLTP 的数据库是应用程序的基石。尽管成熟的数据库通常运行起来都相对稳定,但是否能容忍更多的底层故障,是否能快速地从故障中恢复出来,对应用程序而言是非常重要的。Amazon Aurora 作为亚马逊自研的云原生数据库,除兼容性、性能、扩展性外,它在设计之初,就以极致的可用性作为目标,尽可能减少故障对应用程序的影响。</p>\n<p>Amazon Aurora 在故障恢复方面的设计理念主要包括:</p>\n<p><strong>1.能在较大范围故障时仍然提供服务</strong>:跨3个可用区的6备份存储使它在一个可用区和另一个额外备份发生故障时仍能提供服务;跨区域的全球数据库能在主区域发生故障时快速切换到从区域;</p>\n<p><strong>2.加快故障恢复的速度</strong>:将数据拆分成10 GB 粒度存储单元使单个存储单元能在秒级别恢复;通过分布式存储进行并行恢复等;快速找到健康计算节点先进行节点替换来使整个集群尽快提供服务。</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/a3096a44364b4ed3933d3bc3296c37ae_44ec984ec84de1242543609f16ac00b7.png\" alt=\"44ec984ec84de1242543609f16ac00b7.png\" /></p>\n<p>Amazon Aurora 的架构示意图如上所示,它以端点 endpoint 的形式对外提供服务,用户可以通过读写端点来访问 Aurora 写节点;通过只读端点访问 Aurora 读节点。当写节点发生故障时,Aurora 会进行如下的 failover 过程:</p>\n<ol>\n<li>根据不同节点 failover 优先级和复制延迟来选择一个读节点,将其提升成新的写节点。因为该节点角色发生切换,需要重启该节点。</li>\n<li>尝试恢复原来的写节点并让它成为新的读节点。这里也会涉及到节点重启。</li>\n<li>待新的写节点重启成功以后,定位读写端点指向新的写节点。这里涉及到域名的更新,依赖于 Route53 的实现。</li>\n<li>待新的读节点重启成功以后,定位只读端点指向新的读节点。这里同样涉及到域名更新,依赖于 Route53 的实现。</li>\n</ol>\n<p>通常情况下,Aurora 的 failover 通常能在30-60秒内完成,能对应用程序产生较低影响。那么,是否有可能进一步加快 Aurora failover 的速度呢?本篇文章会重点介绍如何在 Aurora 进行故障恢复的时候避免或者减少 DNS 的影响(步骤3),从而加快故障切换速度。具体内容涵盖故障切换各步骤的时间消耗、花费时间的分析排查以及减少对应用程序影响的几种方案。</p>\n<h3><a id=\"2_Aurora_failover__20\"></a><strong>2. Aurora failover 的过程</strong></h3>\n<p>在 Aurora 发生 failover 的时候,我们可以通过观测 Event 事件来查看 failover 过程中 Aurora 的读写节点究竟发生了哪些操作。以一写一读的集群为例,如果我们在控制台点击 failover,可以观测到如下事件。</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/3e2e2909abb74bf9b26ec0ffa6abe8f6_5a273f20d6022a86431380ed84daf6bb.png\" alt=\"5a273f20d6022a86431380ed84daf6bb.png\" /></p>\n<p>其中,aurora-2092-binlog-on 为 Aurora 集群,aurora-2092-binlog-on-instance-1 为原来的读节点,aurora-2092-binlog-on-instance-1-us-east-1b 为原来的写节点。</p>\n<p>从事件中我们可以看到,当 Aurora 集群发生故障切换时,会先后有如下事件生成:</p>\n<ol>\n<li>集群开始 failover</li>\n<li>原来读节点重启(会成为新的写节点)</li>\n<li>原来写节点重启(会成为新的读节点)</li>\n<li>新写节点重启成功</li>\n<li>新读节点重启成功</li>\n<li>集群 failover 完成</li>\n</ol>\n<p>整个集群 failover 过程在29秒完成。其中,新的写节点重启成功用了6秒,也就是新的写节点在6秒以后就可以接受请求。但是,由于用户访问 Aurora 是利用域名的方式(Aurora 的读写域名和只读域名),新写节点启动后需要更新域名,而域名更新需要一定的时间,所以应用程序用域名连接 Aurora 时体验到的连接中断时间会高于6秒。</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/0565dc6ca2e24f35bfdd011bbcffe788_0ffb9706a80f5300f6389deab0610e98.png\" alt=\"0ffb9706a80f5300f6389deab0610e98.png\" /></p>\n<h3><a id=\"3__41\"></a><strong>3. 如何分析故障切换时,时间消耗在哪里?</strong></h3>\n<p>当遇到 Aurora 集群故障切换影响应用程序较长时间时,我们可以采用如下几步甄别最长的时间花费在哪里,再对症下药,进行优化。</p>\n<h4><a id=\"31__43\"></a><strong>3.1 信息收集</strong></h4>\n<p>除了第2节讲的对 event 的观测以外,我们可以在访问 Aurora 集群的客户端环境中打印 DNS 对应 IP 的信息,来获得 DNS 域名切换的时间。</p>\n<p>可以采用如下代码:</p>\n<pre><code class=\"lang-\">cat nslookup.sh \n#!/bin/bash\nwhile true\ndo\nrwendpoint=$(nslookup aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com)\nroendpoint=$(nslookup aurora-2092-binlog-on.cluster-ro-cttuqplcao81.us-east-1.rds.amazonaws.com)\nnow=$(date +"%Y-%m-%d %H:%M:%S")\necho "$now \\n $ rwendpoint \\n $ roendpoint "\necho "----------------------"\nsleep 1\ndone\n</code></pre>\n<p>另外,Aurora 的写节点和读节点的信息可以从这张系统表中 information_schema.replica_host_status 推理得到,也可以设计类似于监控域名变化的逻辑实时监控读写节点的状态。这张表里读写节点的变化是不依赖于 DNS 的,应该与 Event 中的节点重启的时间能够匹配。</p>\n<pre><code class=\"lang-\">MySQL [(none)]> select server_id AS Instance_Identifier, if(session_id = 'MASTER_SESSION_ID','writer', 'reader') as Role from information_schema.replica_host_status;\n+---------------------------------------------+--------+\n| Instance_Identifier | Role |\n+---------------------------------------------+--------+\n| aurora-2092-binlog-on-instance-1 | writer |\n| aurora-2092-binlog-on-instance-1-us-east-1b | reader |\n+---------------------------------------------+--------+\n2 rows in set (0.00 sec)\n</code></pre>\n<h4><a id=\"32__71\"></a><strong>3.2 分析排查</strong></h4>\n<p>Aurora 的 event 发生的时间是分析排查的基础。总体来讲,进行分析排查可以分两步走:</p>\n<p>首先判断是否 Aurora 新写节点启动本身消耗时间比较久。对比集群开始 failover 的时间和新写节点重启完成的时间。如果此部分花费时间过长,有可能是在发生故障时应用的并发负载比较大,所以新的写节点启动之前需要处理发生故障时正在执行的事务的信息,比如 redolog, undolog 以及 binlog 的处理和恢复等。对于这种情况,可以考虑进行应用负载的优化或者是查看 Aurora 新的版本是否有相应的优化,考虑升级。例如,Aurora 的版本1.23以上版本及2.08以上版本对于大的事务(单个事务对应的 binlog 文件大于0.5 GB)恢复速度有明显提升,如果您的 Aurora 集群中打开了 binlog 并且有大事务,可以考虑升级 Aurora 版本。或者如果 binlog 不是必须的,可以直接通过参数修改把 binlog 关闭。</p>\n<p>接下来判断故障恢复时间是否由 DNS 更新延迟引发。可以结合 Aurora event 打印时间和 nslookup.sh 的输出对比进行分析。以第2节的 failover 的事件发生时为例,nslookup.sh 的脚本输出为:</p>\n<pre><code class=\"lang-\">2022-04-22 09:37:11 \\n Server: 172.31.0.2\nAddress: 172.31.0.2#53\n\nNon-authoritative answer:\naurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com canonical name = aurora-2092-binlog-on-instance-1-us-east-1b.cttuqplcao81.us-east-1.rds.amazonaws.com.\nName: aurora-2092-binlog-on-instance-1-us-east-1b.cttuqplcao81.us-east-1.rds.amazonaws.com\nAddress: 172.31.11.251 \\n Server: 172.31.0.2\nAddress: 172.31.0.2#53\n\nNon-authoritative answer:\naurora-2092-binlog-on.cluster-ro-cttuqplcao81.us-east-1.rds.amazonaws.com canonical name = aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com.\nName: aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com\nAddress: 172.31.12.155\n\n----------------------\n2022-04-22 09:37:12 \\n Server: 172.31.0.2\nAddress: 172.31.0.2#53\n\nNon-authoritative answer:\naurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com canonical name = aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com.\nName: aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com\nAddress: 172.31.12.155 \\n Server: 172.31.0.2\nAddress: 172.31.0.2#53\n\nNon-authoritative answer:\naurora-2092-binlog-on.cluster-ro-cttuqplcao81.us-east-1.rds.amazonaws.com canonical name = aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com.\nName: aurora-2092-binlog-on-instance-1.cttuqplcao81.us-east-1.rds.amazonaws.com\nAddress: 172.31.12.155\n</code></pre>\n<p>我们可以看到,在9:37:12的时候发生的读写 DNS 域名的切换,读写 DNS 均指向了新的写节点,在此之前,读写 DNS 指向的还是原来的写节点。事实上,从第2节我们可以看到,新的写节点在9:37:03的时候就已经重启成功了,所以对于应用程序来说,这9秒的时间是在等待 DNS 的更新。</p>\n<h3><a id=\"4__DNS__Failover__110\"></a><strong>4. 减少 DNS 切换的影响,提升 Failover 的速度</strong></h3>\n<p>如果经过第3节的分析发现 DNS 的切换速度比较慢,也就是写节点重启成功以后 DNS 还需要经过一段时间才能更换,我们可以采用下面几种方式进行改进。</p>\n<h4><a id=\"41__DNS_TTL__113\"></a><strong>4.1 控制客户端 DNS TTL 的时间</strong></h4>\n<p>检查客户端是否缓存了 DNS 及设置时间。如果设置较久的话,可以考虑将客户端的 DNS 的 TTL 调小。如果您的客户端应用程序正在缓存数据库实例的域名服务 (DNS) 数据,请将生存时间 (TTL) 值设置为小于 30 秒。数据库实例的底层 IP 地址在故障转移后可能会发生变化。因此,如果您的应用程序试图连接到不再使用的 IP 地址,那么缓存 DNS 数据达较长时间可能导致连接失败。如果连接使用读取器终端节点,并且只读副本实例之一处于维护状态或已删除,那么具有多个只读副本的 Aurora 数据库集群也会遭遇连接失败。</p>\n<h4><a id=\"42__RDS_Proxy_115\"></a><strong>4.2 使用 RDS Proxy</strong></h4>\n<p>RDS Proxy 是基于 Aurora/RDS 之上提供的一个代理层,它有三个特性:</p>\n<p>1)连接池能够实现连接的多路复用。如果应用对数据库的并发请求比较多,直接打到 Aurora 数据库上会耗费很多资源。可以使用 RDS Proxy 来实现连接复用,支持更多的并发应用,并减少新建连接的需要。</p>\n<p>2)增强的安全性。如果不希望直接把底层数据库的密码直接暴露,可以使用 RDS Proxy,通过使 RDS Proxy 访问 Secrets Manager 里的密码来增强安全性。</p>\n<p>3)快速的故障恢复时间。RDS Proxy 避开了 Aurora 发生故障切换时节点切换带来的域名 DNS 记录更新的问题,用户通过连接 RDS Proxy 可以避免域名更新的时间消耗。</p>\n<p>在现有的 Aurora 集群上开启 RDS Proxy,您可以参考此篇文档中的步骤来实现。应用程序端所做的更改是将 Aurora读写的端点替换成 RDS Proxy 的端点,因为 RDS Proxy 也提供对 Aurora 只读端点的访问和封装。如果您的应用程序中使用了 Aurora 的只读端点,也是可以使用 RDS Proxy 的读取端点来进行配置的。</p>\n<p>为测试 RDS Proxy 的效果,可以利用类似下面小程序来对比在Aurora故障恢复期间直接连接 Aurora 集群和连接 RDS Proxy 对应的影响。程序很简单,每隔 1s 连接到 Aurora 或者 RDS Proxy 集群运行一次 update+select now() 的操作。连接到 Aurora 的程序:</p>\n<p>文档:<br />\n<a href=\"https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/AuroraUserGuide/rds-proxy-setup.html\" target=\"_blank\">https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/AuroraUserGuide/rds-proxy-setup.html</a></p>\n<pre><code class=\"lang-\">cat testAurora.sh\n#!/bin/bash\nwhile true\ndo \n mysql -h aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com -uadmin -p12345678 -Dlili -e "update wr_table_aurora set c='new' where a=2; select now();"\n now=$(date +"%T")\n echo "update done: $now"\n sleep 1\ndone\n</code></pre>\n<p>在第2节的故障恢复的恢复过程中,可以看到连接到脚本的执行输出为:</p>\n<pre><code class=\"lang-\">./testAurora.sh >testAurora.out\n[ec2-user@ip-172-31-1-113 rdsproxy-failover]$ ./testAurora.sh >testAurora.out\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 2003 (HY000): Can't connect to MySQL server on 'aurora-2092-binlog-on.cluster-cttuqplcao81.us-east-1.rds.amazonaws.com' (111)\nERROR 1290 (HY000) at line 1: The MySQL server is running with the —read-only option so it cannot execute this statement\nERROR 1290 (HY000) at line 1: The MySQL server is running with the —read-only option so it cannot execute this statement\nERROR 1290 (HY000) at line 1: The MySQL server is running with the —read-only option so it cannot execute this statement\nERROR 1290 (HY000) at line 1: The MySQL server is running with the —read-only option so it cannot execute this statement\nERROR 1290 (HY000) at line 1: The MySQL server is running with the —read-only option so it cannot execute this statement\n</code></pre>\n<p>查看 testAurora.out 的输出片段:</p>\n<pre><code class=\"lang-\">now()\n2022-04-22 09:36:57\nupdate done: 09:36:57\nnow()\n2022-04-22 09:36:58\nupdate done: 09:36:58\nupdate done: 09:36:59\nupdate done: 09:37:00\nupdate done: 09:37:01\nupdate done: 09:37:02\nupdate done: 09:37:03\nupdate done: 09:37:04\nupdate done: 09:37:05\nupdate done: 09:37:06\nupdate done: 09:37:08\nupdate done: 09:37:09\nupdate done: 09:37:10\nupdate done: 09:37:11\nupdate done: 09:37:12\nnow()\n2022-04-22 09:37:13\nupdate done: 09:37:13\nnow()\n2022-04-22 09:37:14\nupdate done: 09:37:14\n</code></pre>\n<p>从9:36:59一直到9:37:12,应用程序一直是不可用的状态,一开始会报后台数据库连接不上的错误,然后会报只读 read-only 错误,原因在于在原来的写节点重启的过程中,应用程序是不停去连接的,所以报连接失败错误,而当写节点也就是新的读节点重启成功以后,因为域名还在指向它,所以会报只读错误,直至9:37:12域名更新以后应用程序才恢复正常。</p>\n<p>使用 RDS Proxy 测试的脚本内容为:</p>\n<pre><code class=\"lang-\">cat testRDSProxy.sh\n#!/bin/bash\nwhile true\ndo \n mysql -h aurora-proxy-2.proxy-cttuqplcao81.us-east-1.rds.amazonaws.com -uadmin -p12345678 -Dlili -e "update wr_table set c='new' where a=2; select now();"\n now=$(date +"%T")\n echo "update done: $now"\n sleep 1\ndone\n</code></pre>\n<p>在 failover 的过程中运行脚本:</p>\n<pre><code class=\"lang-\">./testRDSProxy.sh &gt;testRDSProxy.out\n</code></pre>\n<p>查看 testRDSProxy.out 的输出片段:</p>\n<pre><code class=\"lang-\">2022-04-22 09:36:57\nupdate done: 09:36:57\nnow()\n2022-04-22 09:36:58\nupdate done: 09:36:58\nnow()\n2022-04-22 09:37:03\nupdate done: 09:37:03\nnow()\n2022-04-22 09:37:04\nupdate done: 09:37:04\nnow()\n2022-04-22 09:37:05\nupdate done: 09:37:05\nnow()\n</code></pre>\n<p>应用程序同样在9:36:59开始没有结果返回,但是在9:37:03即恢复正常,这个时间也就是第2节提到的新写节点启动的时间,4秒的 failover 时间相对于直接连接 Aurora 集群的13秒的时间节约了9秒的时间。此外,在使用 RDS Proxy 的过程中,从9:36:59到9:37:03之间的应用并没有报错信息,这是因为 RDS Proxy 会在底层 Aurora 故障切换的过程中对新发送过来的应用程序进行缓存,当新的写节点启动后,直接将缓存的 SQL 语句发送给新的写节点,这样能一定程度上降低应用程序直接出错带来的影响。</p>\n<p>RDS Proxy 的实现逻辑是它能够动态连接到底层 Aurora 数据库的各个实例,进而在实例状态发生变化时及时捕捉,并更新内部的路由定向机制,所以成功避免了 DNS 域名切换的时间,提升了 Aurora 故障切换的速度。此外,RDS Proxy 是 Serverless 的架构,开启较为简单,而且可以根据应用程序对数据库的负载弹性伸缩,可以做为您在考虑降低 failover DNS 切换时间的一个选择。</p>\n<h4><a id=\"43__227\"></a><strong>4.3 使用智能驱动</strong></h4>\n<p>如果您的应用程序采用的是 Java 语言,除 RDS Proxy 外,也可以考虑使用亚马逊提供的<strong>智能驱动</strong>。智能驱动在连接到 Aurora 集群时,会拿到整个集群的拓扑和各个节点的角色信息(是写节点还是读节点)维持在缓存中。有了这个拓扑,在节点发生变化时,就可以快速拿到变化的节点的角色信息,而无需再依赖的 DNS 的解析。</p>\n<p>智能驱动:<br />\n<a href=\"https://awslabs.github.io/aws-mysql-jdbc/\" target=\"_blank\">https://awslabs.github.io/aws-mysql-jdbc/</a></p>\n<p><img src=\"https://dev-media.amazoncloud.cn/724f78690d694759812e84864a1ab1df_4caa60065ff892031f7ba7e15e937df1.png\" alt=\"4caa60065ff892031f7ba7e15e937df1.png\" /></p>\n<p>上图是智能驱动自身的逻辑示意,应用程序连接到它时,会维护一个逻辑的连接和物理的连接。比如现在的写节点是 C,但同时它的拓扑缓存中会存放着节点 A 和 B 的信息,这样如果节点 C 发生了故障,能够及时检测并将物理连接切换到 A 或 B 节点(取决于 Aurora 决定 failover 到哪个节点)而逻辑连接是保持不变的。</p>\n<p>智能驱动与 MySQL 的普通驱动使用方法是一样的,您只需要更换驱动,并将连接字符串更换为如下格式即可。注意这里的 url 的字符串前面是 jdbc:mysql:aws。缺省情况下,故障切换的能力是开启的,也可以通过参数调整的方式进行关闭。</p>\n<p>jdbc:mysql:aws://* 集群名称*.<em>集群 id</em>.us-east-2.rds.amazonaws.com:3306/* 数据库名 *?useSSL=false&characterEncoding=utf-8</p>\n<p>这篇<strong>博客</strong>里测试对比了智能驱动与普通依赖于 DNS 解析的驱动对应用程序影响的时间。和 RDS Proxy 类似,平均影响时间会降至4秒。详细测试方法可以查阅该博客。</p>\n<p>博客:<br />\n<a href=\"https://aws.amazon.com/cn/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/\" target=\"_blank\">https://aws.amazon.com/cn/blogs/database/improve-application-availability-with-the-aws-jdbc-driver-for-amazon-aurora-mysql/<br />\n</a><br />\n<img src=\"https://dev-media.amazoncloud.cn/5ae23480dd364299884b7f80ef0296df_image.png\" alt=\"image.png\" /><br />\n亚马逊的智能驱动是开源的,采用的 Apache 协议。如果您的应用程序不是用 Java 语言,也可以采用类似逻辑来进行实现,即拿到 Aurora 集群的拓扑结构和各个节点的角色信息,然后阶段性连接(比如每隔1秒),看是否发生变化,如果发生变化构建新的物理连接。</p>\n<h4><a id=\"44__250\"></a><strong>4.4 事件捕捉,触发连接重置</strong></h4>\n<p>另外一种可以考虑的思路是利用事件监控机制,触发 Lambda 方法,在 Lambda 方法中检测到新写节点重启成功或者是集群重启成功以后,触发应用程序的重联机制。这篇<strong>博客</strong>的最后一个小节里提供了一个示例来使用 Lambda 方法来触发 ShardingSphere-Proxy 到 Aurora 的数据源创建的 DistSQL 语句,从而避免了只读错误不断抛出的问题。</p>\n<p>博客:<br />\n<a href=\"https://aws.amazon.com/cn/blogs/china/make-further-progress-shardingsphere-proxy-chapter-of-amazon-auroras-reading-and-writing-ability-expansion/\" target=\"_blank\">https://aws.amazon.com/cn/blogs/china/make-further-progress-shardingsphere-proxy-chapter-of-amazon-auroras-reading-and-writing-ability-expansion/<br />\n</a><br />\n如果有些语言的驱动进行了 IP 地址的缓存,在 DNS 切换时并不能检测到,仍然使用原来 IP 进行连接的话,会一直报只读的错误。这种情况,也可以考虑使用 SQL 错误码分析并重置连接的方式来做为一个短期解决问题的方案。以 MySQL 8.0 为例,一般需要关注的信息有:</p>\n<ul>\n<li>1836错误码。数据库在只读模式。</li>\n<li>1792错误码。事务在只读模式。</li>\n<li>Analyze/optimize 表的输出中含有 read-only 的错误。</li>\n</ul>\n<p>下面是抛出错误的一些示例:</p>\n<pre><code class=\"lang-\">MySQL [(none)]> create database test;\nERROR 1836 (HY000): Running in read-only mode\n\nMySQL [aaa]> begin;\nQuery OK, 0 rows affected (0.00 sec)\nMySQL [aaa]> select count() from wr_table;\n+----------+\n| count() |\n+----------+\n| 1277 |\n+----------+\n1 row in set (0.01 sec)\n\nMySQL [aaa]> insert into wr_table values(51);\nERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.\n\nMySQL [aaa]> analyze table wr_table;\n+--------------+---------+----------+----------------------------------------------------------------+\n| Table | Op | Msg_type | Msg_text |\n+--------------+---------+----------+----------------------------------------------------------------+\n| aaa.wr_table | analyze | Warning | InnoDB: Running in read-only mode |\n| aaa.wr_table | analyze | Error | Running in read-only mode |\n| aaa.wr_table | analyze | Error | Unable to store dynamic table statistics into data dictionary. |\n| aaa.wr_table | analyze | status | Unable to write table statistics to DD tables |\n+--------------+---------+----------+----------------------------------------------------------------+\n4 rows in set (0.00 sec)\n\nMySQL [aaa]> optimize table wr_table;\n+--------------+----------+----------+-----------------------------------+\n| Table | Op | Msg_type | Msg_text |\n+--------------+----------+----------+-----------------------------------+\n| aaa.wr_table | optimize | Warning | InnoDB: Running in read-only mode |\n| aaa.wr_table | optimize | Warning | InnoDB: Running in read-only mode |\n| aaa.wr_table | optimize | Error | Running in read-only mode |\n| aaa.wr_table | optimize | error | Corrupt |\n+--------------+----------+----------+-----------------------------------+\n4 rows in set (0.00 sec)\n</code></pre>\n<p>下面这张表里总结了几种不同的方案和各自的优缺点,供您在降低 Aurora 故障切换 DNS 切换时间的影响时进行参考。总体而言,如果应用程序是 Java 语言写的,推荐您使用智能驱动;如果是非 Java 语言,推荐您使用 RDS Proxy;如果对性能要求很高,可以在短期内考虑事件触发重联,长期考虑参照亚马逊的智能驱动逻辑自己实现一个依赖于 Aurora 系统表元信息变更而不是域名切换的智能驱动。</p>\n<p><img src=\"https://dev-media.amazoncloud.cn/2353a4e2ca664470818f9b02251c5467_image.png\" alt=\"image.png\" /></p>\n<h3><a id=\"5__307\"></a><strong>5. 结语</strong></h3>\n<p>Amazon Aurora 独特的计算存储分离的架构、日志即数据库的设计以及其他的一些优化,能够使它在发生主节点故障时进行快速的故障恢复,然而切换后的节点是否能够快速地被应用程序检测到是受 DNS 域名解析的限制的。</p>\n<p>本文分析了 Aurora 故障切换时发生的 event 事件,提出了排查的思路,并提出了几种不同的方案来减轻 DNS 解析的影响,比如减少客户端 DNS 缓存 TTL,增加 RDS Proxy 层避免 DNS 的影响,使用智能驱动来绕过 DNS 解析的时间延迟,或者是通过事件通知机制、错误码分析等。您可以在实际应用中,针对不同的需求选择合适的方案来更近一步地利用到 Aurora 的快速故障切换,提升数据库的可用性,降低对应用程序的影响。</p>\n<h3><a id=\"_312\"></a><strong>本篇作者</strong></h3>\n<p><strong>马丽丽</strong><br />\n亚马逊云科技数据库解决方案架构师,十余年数据库行业经验,先后涉猎 NoSQL 数据库 Hadoop/Hive、企业级数据库DB2、分布式数仓Greenplum/Apache HAWQ 以及亚马逊云原生数据库的开发和研究。</p>\n<p><a href=\"https://aws.amazon.com/cn/blogs/china/be-vigilant-in-times-of-peace-amazon-aurora-fault-recovery-reduces-the-impact-of-dns-switching-on-applications/\" target=\"_blank\">阅读原文</a></p>\n"}