Author: zhenpin
从MySQL 8.0.22开始,官方对主备复制链路的容错性做了进一步的增强,支持了一个称为“Automatic Asynchronous Replication connection failover”的自动连接容错功能。该功能在两个版本中分两个阶段进行了实现,具体来说:
以官方blog中的图片为例。假设当前我们有两个数据中心,分别在New York和London。New York是主机房,部署了多个MySQL的副本,S1为主库接收读写请求,S2为从库,他们之间以C1链路进行连接,C1链路可以是异步复制或者Group Replication。如果是Group Replication,New York机房还会有S3,下图暂时省略掉。London机房有一个只读节点R1,由于New York和London是不同的数据中心,考虑到延迟的问题,MGR不适合跨地域的场景,他们之间一般会用异步模式进行复制。 正常情况下R1从S1通过C2链路同步数据。当S1发生故障时,C2链路的IO线程会报错。在传统的场景下,一般有两种处理方法:R1发出IO线程不可用的告警,通知DBA手动执行CHANGE MASTER命令修正复制链路;或者在London部署一个第三方监控工具,定期探测New York机房的实例状态,探测到S1故障后,执行London的链路切换。 S1宕机后,New York中心机房会触发高可用机制,Group Replication会自动将主节点切换到S2,如果是异步复制则会借助MySQL MHA等外部工具。开启Automatic connection failover功能后,我们会给R1的源端配置一个包括S1、S2的可选列表。考虑到短期网络抖动的可能,C2链路会在一定时间内进行重连。当重试时间超过一定阈值后,R1会自动和S2建立一个新的链路C3,继续同步New York中心机房的数据,无需外部系统介入。
首先我们拉起两个实例S1和S2,假设端口分别是13000和13100。 在S1上创建复制账号。
mysql> CREATE USER 'replicator'@'%' IDENTIFIED BY 'mypass';
Query OK, 0 rows affected (0.01 sec)
mysql> GRANT ALL ON *.* TO 'replicator'@'%';
Query OK, 0 rows affected (0.00 sec)
在S2上创建S1→S2的主备复制关系。
mysql> CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=13000, MASTER_USER='replicator', MASTER_PASSWORD='mypass', GET_MASTER_PUBLIC_KEY=1, MASTER_AUTO_POSITION = 1 FOR CHANNEL 'c1';
Query OK, 0 rows affected, 8 warnings (0.01 sec)
mysql> START SLAVE FOR CHANNEL 'c1';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 127.0.0.1
Master_User: replicator
Master_Port: 13000
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Auto_Position: 1
Channel_Name: c1
1 row in set, 1 warning (0.00 sec)
拉起实例R1,假设端口是13200。执行如下命令创建复制链路。之前提到过当重试时间超过一定阈值后,才会触发自动连接容错,该阈值依赖两个参数,MASTER_RETRY_COUNT和MASTER_CONNECT_RETRY。其中MASTER_RETRY_COUNT默认值是86400,MASTER_CONNECT_RETRY默认值是60秒,即默认场景下,60天之后才超出重连阈值。因此我们把他调小为2分钟。
mysql> CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=13000, MASTER_USER='replicator', MASTER_PASSWORD='mypass', GET_MASTER_PUBLIC_KEY=1, MASTER_AUTO_POSITION = 1, MASTER_RETRY_COUNT=2, MASTER_CONNECT_RETRY=60 FOR CHANNEL 'c2';
Query OK, 0 rows affected, 10 warnings (0.01 sec)
mysql> START SLAVE FOR CHANNEL 'c2';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 127.0.0.1
Master_User: replicator
Master_Port: 13000
Connect_Retry: 60
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Master_Retry_Count: 2
Auto_Position: 1
Channel_Name: c2
1 row in set, 1 warning (0.00 sec)
然后在R1调用官方提供的UDF(asynchronous_connection_failover_add_source)添加备选源端,还可以通过系统表查询已添加的地址。相关UDF的使用方法和参数可以参考官方文档 Functions which Configure the Source List。
mysql> SELECT asynchronous_connection_failover_add_source('c2', '127.0.0.1', 13000, '', 50);
+-------------------------------------------------------------------------------+
| asynchronous_connection_failover_add_source('c2', '127.0.0.1', 13000, '', 50) |
+-------------------------------------------------------------------------------+
| The UDF asynchronous_connection_failover_add_source() executed successfully. |
+-------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT asynchronous_connection_failover_add_source('c2', '127.0.0.1', 13100, '', 50);
+-------------------------------------------------------------------------------+
| asynchronous_connection_failover_add_source('c2', '127.0.0.1', 13100, '', 50) |
+-------------------------------------------------------------------------------+
| The UDF asynchronous_connection_failover_add_source() executed successfully. |
+-------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select * from mysql.replication_asynchronous_connection_failover;
+--------------+-----------+-------+-------------------+--------+--------------+
| Channel_name | Host | Port | Network_namespace | Weight | Managed_name |
+--------------+-----------+-------+-------------------+--------+--------------+
| c2 | 127.0.0.1 | 13000 | | 50 | |
| c2 | 127.0.0.1 | 13100 | | 50 | |
+--------------+-----------+-------+-------------------+--------+--------------+
2 rows in set (0.00 sec)
调用CHANGE MASTER在R1开启自动连接容错功能,之后replication_connection_configuration中SOURCE_CONNECTION_AUTO_FAILOVER列会显示为1,replication_connection_status中SERVICE_STATE会显示为ON。
mysql> CHANGE MASTER TO SOURCE_CONNECTION_AUTO_FAILOVER=1 FOR CHANNEL 'c2';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> SELECT CHANNEL_NAME, SOURCE_CONNECTION_AUTO_FAILOVER FROM performance_schema.replication_connection_configuration;
+--------------+---------------------------------+
| CHANNEL_NAME | SOURCE_CONNECTION_AUTO_FAILOVER |
+--------------+---------------------------------+
| c2 | 1 |
+--------------+---------------------------------+
1 row in set (0.00 sec)
mysql> select CHANNEL_NAME, SERVICE_STATE from performance_schema.replication_connection_status\G
*************************** 1. row ***************************
CHANNEL_NAME: c2
SERVICE_STATE: ON
1 row in set (0.00 sec)
进行破坏性测试,我们主动kill实例S1,并观察R1的状态。
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Reconnecting after a failed master event read
Master_Host: 127.0.0.1
Master_User: replicator
Master_Port: 13000
Slave_IO_Running: Connecting
Slave_SQL_Running: Yes
Last_IO_Error: error reconnecting to master 'replicator@127.0.0.1:13000' - retry-time: 60 retries: 1 message: Can't connect to MySQL server on '127.0.0.1:13000' (111)
Channel_Name: c2
1 row in set, 1 warning (0.00 sec)
等待2分钟左右,我们可以看到R1成功连接到了S2,功能测试完成。
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 127.0.0.1
Master_User: replicator
Master_Port: 13100
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Channel_Name: c2
1 row in set, 1 warning (0.01 sec)
前面的步骤只用到了添加节点的接口,除此之外,官方还支持删除节点、添加MGR节点、删除MGR节点的功能,具体的细节可以参考上面提到的官方文档。
本文主要介绍了新版本MySQL 8.0提供的Automatic connection failover相关功能,可以看到该功能对跨数据中心复制链路的高可用有一定的帮助。目前本文还未深入研究内部的实现方案,只是做了初步的功能测试,直观上感觉类似功能的代码实现并不复杂,感兴趣的朋友可以一起进一步阅读源码。在此引申两点思考:
阿里云在MySQL高可用方面已经有了多年的探索经验。阿里云数据库有一个项目叫X-Cluster,对应云上的产品叫RDS三节点企业版。我们的三节点产品在2017年就支持通过以Learner角色接入一致性协议,实现异步只读节点。Learner不依赖GTID,依靠Paxos协议保证只读节点和主库的数据一致性,解决自动连接容错的问题。基于Paxos协议,在网络分区的场景下,新主会主动和Learner通信,创建新的同步链路。此外还支持级联复制,备机房的只读节点既可以挂载在主机房的读写节点上,也可以级联挂载在任意其他只读节点上,从而搭建多机房单元化的部署模式。对于级联复制,同样借助一致性协议实现自动容错。可以看到,在跨机房、跨地域部署的高可用技术上,阿里云数据库已经走在了官方的前面,对上文提到的两点思考都有系统性的解决方案,具体的技术细节可以参考我之前写的文章 RDS三节点企业版·一致性协议 和 RDS三节点企业版·Learner 只读实例。
数据库上云是大势所趋,当前基本所有公有云提供的MySQL服务都具备高可用能力,平台或多或少的解决了复制链路容灾的问题,无需用户或运维人员介入。官方的Automatic connection failover功能可以说是姗姗来迟,但也并不是一无是处,在这里我抛砖引玉,提供一个未来展望的方向。
随着企业在数字化转型的过程中,对合规、数据安全、功能定制化等需求逐步上升,混合云的概念现在火热起来。简单来说,混合云将私有云和公有云结合起来,中间通过专用网络进行数据同步,其优势不再赘述。以数据库为场景举个例子,客户基于开源版本的MySQL在私有云部署敏感业务,同时在公有云购买云服务商的MySQL高可用版本,以支持弹性、高增长的新业务。此外,客户希望自己手里有完整的数据,会异步的将公有云的数据同步到私有云。由于数据同步链路脱离于云服务商的系统,公有云执行容灾切换或弹性扩缩容后,如何保证这个中间链路的可靠性就成为了一个核心问题。考虑到客户不一定有足够的研发能力,所以一般混合云解决方案会附带售卖如DTS这样的数据同步服务。现在有了Automatic connection failover后,客户可以将私有云MySQL的备选源端配置为公有云的多个高可用节点,无需第三方的服务就能实现较为稳定可靠的同步链路,是不是可以省下一大笔钱。 那么本文就说到这里,随着MySQL的完善和发展,我相信未来会有更多的业务场景被不断发掘出来。