使用高级 JDBC 包装驱动程序在升级 Amazon RDS 多可用区数据库集群时实现一秒或更短的停
实现亚马逊 RDS MultiAZ 数据库集群升级时的停机时间不超过一秒
关键要点
使用高级JDBC包装驱动可在进行亚马逊 RDS 的 MultiAZ 集群升级时,自动切换到新写入器,从而将停机时间减少到一秒或更少。必须在 PostgreSQL 154 及以上版本的 RDS MultiAZ 数据库集群中安装 rdstools 扩展。驱动程序利用数据库的拓扑信息实现快速切换,并支持处理故障转移后的连接问题。在进行 Amazon RDS MultiAZ 数据库集群的次要版本升级时,当前的写入器连接会切换到新升级的读取器。客户可以选择连接到集群的写入器端点或读取器端点,通常会连接到写入器端点,该端点指向当前的写入实例。如果由于参数更改或故障转移而导致集群重启,则其中一个读取器会被提升为新的写入器。这时,集群的写入器端点会更新为指向新的写入器,更新是在 DNS 中修改实例端点。然而,由于 DNS 传播的固有延迟,这个过程可能会耗时长达 30 秒。通常,应用程序需要编写逻辑在数据库暂时不可用后重新连接。
高级 JDBC 包装驱动能自动检测并切换到新写入器。通过利用 RDS MultiAZ 数据库内部的拓扑信息,该驱动程序在没有副本延迟的情况下,可以在约一秒或更短的时间内完成连接切换。
在之前的文章中,介绍了如何使用驱动程序处理 Amazon Aurora 集群的故障转移。在本篇文章中,我们将展示如何使用 AWS 高级 JDBC 包装驱动,在对 MultiAZ PostgreSQL 或 MultiAZ MySQL 进行次要版本升级时,将停机时间减少到一秒或更少,并讨论新引入的 API。
先决条件
要实现一秒或更少的停机时间,您需要配置一个至少运行 PostgreSQL 154 且具有 rdstools 扩展版本 14 或更高版本的 RDS PostgreSQL MultiAZ 数据库集群。如果您已有这个版本的集群,您需要升级到至少 R3 版本。此外,您需要使用以下 DDL 安装 rdstools 扩展:
sqlCREATE EXTENSION rdstools
RDS for PostgreSQL 集群元数据
为了实现快速切换,我们在亚马逊 RDS for PostgreSQL 中实现了一个拓扑函数。访问此函数需要确保您已安装最新的 rdstools 扩展,版本为 14 或更高,并满足先决条件中列出的受支持数据库引擎版本要求。成功安装 rdstools 扩展后,可以使用以下命令检查拓扑元数据:
sqlSELECT FROM RDSTOOLSSHOWTOPOLOGY(‘ltclientidentifiergt’)
idendpointportdbIDENTIFIER3somedbinstance3aaaaaaaaaaaauswest2rdsamazonawscom5432dbIDENTIFIER2somedbinstance2aaaaaaaaaaaauswest2rdsamazonawscom5432dbIDENTIFIER1somedbinstance1aaaaaaaaaaaauswest2rdsamazonawscom5432在上面的表中,各列的说明如下: id:显示每个实例在 RDS MultiAZ 集群中的 DBIRESOURCEID。 endpoint:列出集群中每个节点的 Amazon Route 53 CNAME。 port:显示与 RDS MultiAZ 集群中每个实例关联的端口号。
参数 clientidentifier 在 showtopology 函数中是可选的,但建议提供。传递客户端标识符可以使 Amazon RDS 跟踪哪些客户端库或代理正在主动查询拓扑。这为 Amazon RDS 开发人员提供了有用的遥测,并有助于未来支持其他客户端的引导。
此外,rdstools 扩展还提供了函数 rdstoolsmultiazdbclustersourcedbiresourceid() 用于指示写入器的资源 ID。
高级 JDBC 包装驱动使用这些拓扑信息直接连接到 RDS MultiAZ 集群中的所有三个实例。新引入的拓扑信息适用于 154、149、1312 及更高版本的亚马逊 RDS for PostgreSQL。若您已有这些版本的集群,需至少升级到 R3 版本。
RDS for MySQL 集群元数据
与亚马逊 RDS for PostgreSQL 类似,mysql 数据库中同样存在名为 rdstopology 的表,包含相同的列信息:id、endpoint 和 port。
要检查拓扑信息,可以使用以下 SQL 命令:

sqlSELECT FROM MYSQLRDSTOPOLOGY
在应用程序中使用高级 JDBC 包装驱动
Maven 坐标如下:
网络魔法梯子xmlltdependencygt ltgroupIdgtsoftwareamazonjdbclt/groupIdgt ltartifactIdgtawsadvancedjdbcwrapperlt/artifactIdgt ltversiongtLATESTlt/versiongtlt/dependencygt
将其作为依赖项添加到项目中。您还需要 MySQL JDBC 驱动程序 或 PostgreSQL JDBC 驱动程序。
使用该驱动程序非常简单。亚马逊 RDS for PostgreSQL 的连接模式如下:
javajdbcawswrapperpostgresql//host/databaseConnection conn = DriverManagergetConnection(jdbcawswrapperpostgresql//host/database props)
而亚马逊 RDS for MySQL 的连接模式如下:
javajdbcawswrappermysql//host/databaseConnection conn = DriverManagergetConnection(jdbcawswrappermysql//host/database props)
在您的代码中需要对以下三种情况做出响应: FailoverFailed:无法建立到新写入器的连接,应用程序需开启新连接。 FailoverSuccess:连接已恢复,应用程序需要重新设置自连接打开后添加的任何会话状态。 TransactionStateUnknown:当故障转移发生时,应用程序正在进行交易并已恢复连接,但交易需再次尝试。
我们在 GitHub 仓库中有许多示例说明如何使用驱动程序。这个示例展示了如何连接并处理异常。
需要注意的是,在设置连接时,要修改 failoverClusterTopologyRefreshRate。该设置决定了驱动程序读取拓扑的频率,默认值为 2000ms。将其设置为 100ms 是必要的,以确保尽快发现新的写入器。
javapropssetProperty(failoverClusterTopologyRefreshRateMs 100)
您可以使用以下代码测试驱动程序的快速切换能力:
javapackage softwareamazondemo
import javasqlConnectionimport javasqlDriverManagerimport javasqlSQLExceptionimport javasqlStatementimport javautilPropertiesimport javautilloggingLogger
import softwareamazonjdbcpluginfailoverFailoverSuccessSQLException
public class Main { private static final Logger LOGGER = LoggergetLogger(MainclassgetName()) final Properties properties = new Properties() propertiessetProperty(user testuser) propertiessetProperty(password testpassword) // 在故障转移期间每 100 毫秒刷新拓扑,以便更快找到新的写入器 propertiessetProperty(failoverClusterTopologyRefreshRateMs 100) propertiessetProperty(loginTimeout 1) // 启用故障转移插件 propertiessetProperty(wrapperPlugins failover)
// 使用 “jdbcawswrapperpostgresql//” 连接 RDS MultiAZ Postgres 集群String connString = jdbcawswrappermysql//ltclusterendpointgt3306/mysqlConnection conn = DriverManagergetConnection(connString properties)} catch (FailoverSuccessSQLException e) { while (true) { try (Statement stmt = conncreateStatement()) { stmtexecuteQuery(SELECT 1) LOGGERinfo([OK] 成功执行查询。) } catch (FailoverSuccessSQLException e) { // 查询执行失败,但 JDBC 包装器成功故障转移到新的写入节点 try (Statement stmt = conncreateStatement()) { stmtexecuteQuery(SELECT 1) } LOGGERinfo([故障转移完成] 连接到新的写入节点。) break } catch (Exception e) { LOGGERwarning(etoString()) } }}下面是如何运行测试及日志输出的示例。此次的停机时间约为 300ms。
使用以下 AWS CLI 命令创建测试集群:bashaws rds createdbcluster dbclusteridentifier myappdb engine mysql engineversion 8028 dbclusterinstanceclass dbm5dlarge storagetype io1 masterusername testuser masteruserpassword testpassword iops 1000 allocatedstorage 100使用实际的数据库用户和密码配置属性。将 connString 替换为实际的集群端点。在与 RDS MultiAZ DB 集群相同 VPC 的 EC2 实例中运行上述演示应用程序。在演示应用程序运行时,通过 AWS 控制台或 AWS CLI 执行数据库引擎次要版本升级。演示应用程序会自动与新的写入器重新连接,停机时间少于一秒。此次故障转移耗时 331ms,您可以在以下日志中看到:Nov 08 2023 113227348 PM softwareamazondemoMain main INFO [OK] 成功执行查询。 Nov 08 2023 114541706 PM softwareamazondemoMain main INFO [OK] 成功执行查询。 Nov 08 2023 114541708 PM softwareamazondemoMain main INFO [OK] 成功执行查询。 Nov 08 2023 114541855 PM softwareamazonjdbchostlistproviderRdsMultiAzDbClusterListProvider processTopologyQueryResults SEVERE 拓扑查询返回无效拓扑 检测不到写入实例。 Nov 08 2023 114542036 PM softwareamazonjdbcpluginfailoverFailoverConnectionPlugin failover SEVERE 由于连接失败,活动 SQL 连接发生改变。请根据需要重新配置会话状态。 Nov 08 2023 114542039 PM softwareamazondemoMain main INFO [故障转移完成] 连接到新的写入节点。
清理
如果您不打算将来使用创建的 MultiAZ DB 集群,请确保将其清理。
结论
借助高级 JDBC 包装驱动中的改进逻辑,您可以在进行 RDS MultiAZ 集群的次要版本升级时,提高应用程序的写入可用性,且无需担心 DNS 传播延迟。将其添加到项目中,并根据上述 Maven 坐标进行集成。欢迎在下面的评论中告诉我们效果如何。