与Oracle DataGuard切换原理类似,主、从复制也分为计划性切换和故障切换两种方式。其中,计划性切换的关键步骤如下列所示,而故障切换则更为简单,只需要单独执行下列的步骤4)即可实现。
1.关闭前端业务
为了尽量规避切换失败的风险,用户必须合理管控切换流程。在切换之前建议先停止前端应用,待程序停止完毕,再检查MySQL数据库中是否仍有残留的外部会话连接。确认清楚后再参考以下命令清理相关线程。
强制断开与MySQL实例交互的所有连接线程,注意其中不包括当前线程:
shell> mysqladmin -uroot -p processlist|awk -F “|” ‘{print $2}’|sed ‘/^$/d’| \
sed ‘1d;$d’|tr “\n” “,”|sed ‘s/[[:space:]]//g;s/,$/\n/’|xargs \
mysqladmin -uroot -p kill
如果出于某些原因不能杀掉全部连接,那么可以根据数据库用户或访问终端进行精确匹配,并将指定的线程断开:
shell> mysqladmin -uroot -p processlist|awk -F “|” ‘{if($3~/user1|user2/) print $2}’\
|tr “\n” “,”|sed ‘s/[[:space:]]//g;s/,$/\n/’|xargs mysqladmin -uroot -p kill
或者
shell> mysqladmin -uroot -p processlist|awk -F “|” ‘{if($4~/host1|host2/) print $2}’\
|tr “\n” “,”|sed ‘s/[[:space:]]//g;s/,$/\n/’|xargs mysqladmin -uroot -p kill
说明:进行精确匹配的数据库用户和访问终端可以是一个也可以是多个。
在某些极端情况下,主、从节点可能会因为I/O线程出现瓶颈而导致复制延时。在该场景下,暴力断开连接时需要保留主库上的Binlog Dump线程,可参考下列命令进行规避:
shell> mysqladmin -uroot -p processlist|awk -F “|” ‘{if($6!~/Binlog Dump/) \
print $2}’|sed ‘/^$/d’| sed ‘1d;$d’|tr “\n” “,”|sed ‘s/[[:space:]]//g;\
s/,$/\n/’|xargs mysqladmin -uroot -p kill
2.主库执行FTWRL以禁止数据写入
在主节点执行全局读锁FTWRL,可以禁止任何数据写入。如果是在非GTID模式下,则需要记录当前二进制日志文件名和位置;如果是在GTID模式下,则需要记录当前已经执行过的GTID。若担心当前持锁线程异常退出,则建议将主库设置为只读模式,之后可选择释放FTWRL。当然,用户也可以选择强制关闭主库,但在极端情况下,这样做会导致主、从库数据不一致的问题。例如,当前复制拓扑中网络带宽配置很低进而出现瓶颈,从库I/O线程获取主库二进制日志出现等待并造成复制延迟,此时停掉主库自然就会引起数据不一致的问题。
mysql> flush table with read lock;
非GTID模式:
mysql> show master status\G;
[..]
File: mysql-binlog.000002
Position: 911094114
[…]
GTID模式:
mysql> show master status\G;
[…]
Executed_Gtid_Set: efb88661-5dba-11ea-b7b5-3a8dd859809f:1-8682
mysql> set global read_only=on;
mysql> set global super_read_only=on;
mysql> unlock tables;
3.从库确认是否存在复制延迟
此时,主库不会产生新的二进制日志,需要在从库上检查复制状态和延迟,并确认I/O和SQL线程状态均为YES。非GTID模式下,需要比较Master_Log_File与Relay_Master_Log_File指标值,同时还要比较Read_Master_Log_Pos和Exec_Master_Log_Pos指标值,若这些选项的指标值都相同,则表明主/从库数据已经达到一致;GTID模式下,虽然可以继续复用基于二进制日志文件名和位置的校验方式,但是我们还是建议使用更为简单和直观的Retrieved_Gtid_Set和Executed_Gtid_Set选项,若这两个选项的最大GTID一致,则同样可以说明主从库数据一致。当然,Seconds_Behind_Master选项也能大致反映延迟情况,不过该指标基于时间模型,在精度上稍微差了些。待主从库两边的数据完全一致之后,便可手动执行切换。
非GTID模式:
mysql> show slave status\G;
[..]
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Master_Log_File: mysql-binlog.000002
Read_Master_Log_Pos: 911094114
Relay_Master_Log_File: mysql-binlog.000002
Exec_Master_Log_Pos: 911094114
[…]
GTID模式:
mysql> show slave status\G;
[..]
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Retrieved_Gtid_Set: efb88661-5dba-11ea-b7b5-3a8dd859809f:7782-8682
Executed_Gtid_Set: efb88661-5dba-11ea-b7b5-3a8dd859809f:1-8682
[…]
4.从节点执行切换
如果该从库不存在级联的二级从库,则在切换成新主库之前,建议使用RESET SLAVE ALL语句清理中继日志和从库上记录的主节点信息,同时使用RESET MASTER语句重置二进制日志,尽可能让新主库的环境简单化。切换之前,需要在确认主、从库数据处于一致状态后,在从节点上执行全局读锁FTWRL并关闭只读,最后再释放FTWRL。此时,从库已经成为了新主库,可正常对外提供服务。
命令如下:
mysql> stop slave;
mysql> reset slave all;
mysql> reset master;
mysql> flush table with read lock;
非GTID模式:
mysql> show master status\G;
[..]
File: mysql-binlog.000001
Position: 154
[…]
GTID模式:
mysql> show master status\G;
[…]
Executed_Gtid_Set:
mysql> set global read_only = off;
mysql> set global super_read_only = off;
mysql> unlock tables;
该切换逻辑非常简单。当切换窗口到来时,先锁住主节点并设置为只读模式,这意味着主节点不会新增数据。当从节点追上主节点,且两节点数据达到一致后,在从节点上停止复制并清空从库信息。为了避免切换前后对从节点二进制日志信息产生混淆,建议在从节点上重置二进制日志并回到初始状态(这是一个可选操作)。接着,执行FTWRL并记录当前二进制日志点位(为切换后的新主节点、新从节点部署复制,这不是一个必须执行的步骤),再关闭从库只读模式(对于从库,强烈建议开启只读模式)并释放FTWRL。至此,从库成为新的主库。
5.恢复新主(原从)和新从(原主)复制同步
如果该切换动作属于计划性切换而不是故障切换,即数据一致性没有受到破坏,那么切换后可以正常恢复复制拓扑中的数据同步。由于在切换过程中使用了RESET MASTER语句,因此这就意味着可以从新主节点上的第一个二进制日志文件的初始位置启动复制。
非GTID模式中,获取上文产生的新主节点上初始二进制日志的文件名和位置,命令如下:
mysql> show master status\G;
[..]
File: mysql-binlog.000001
Position: 154
[…]
构建CHANGE MASTER TO语句,并在新从(原主)节点执行,启动复制(确保复制账户repl存在并拥有复制权限),示例代码如下:
mysql> CHANGE MASTER TO MASTER_HOST=’192.168.113.110′, MASTER_USER=’repl’,\
MASTER_PASSWORD=’Abcd321#’, MASTER_PORT=33061, \
MASTER_LOG_FILE=’mysql-binlog.000001′, MASTER_LOG_POS=154;
mysql> start slave;
mysql> show slave status\G
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
[…]
GTID模式,获取上文产生的新主节点上初始的全局事务标识符,由于该节点业务还未写入,因此初始值为空,命令如下:
mysql> show master status\G;
[…]
Executed_Gtid_Set:
同样需要构建CHANGE MASTER TO语句,并在新从(原主)节点执行,然后启动复制,命令如下:
mysql> CHANGE MASTER TO MASTER_HOST=’192.168.113.110′, MASTER_USER=’repl’, \
MASTER_PASSWORD=’Abcd321#’, MASTER_PORT=33061, MASTER_AUTO_POSITION = 1;
mysql> start slave;
mysql> show slave status\G;
[..]
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Retrieved_Gtid_Set: 72ee2d1e-5de6-11ea-8e8b-ee9652521caf:1-2379
Executed_Gtid_Set: 72ee2d1e-5de6-11ea-8e8b-ee9652521caf:1-2379,
efb88661-5dba-11ea-b7b5-3a8dd859809f:1-8682
[…]
至此,原主节点就成为了新的从节点。本节的复制同步恢复流程,依赖于计划性切换场景,对于故障切换并不适用。因为是计划性切换,类似于Oracle的物理DataGuard,因此只要复制环境没有被破坏就支持回切,若有回切需求,则按上述流程再走一遍即可。
6.修改IP地址
如果前端使用了MySQL服务器的物理地址对外提供服务,那么切换后就需要修改IP地址了。要么在前端应用中修改连接配置,要么直接修改服务器的物理地址,这两种方式相对来说都比较麻烦。若使用的是VIP连接,那么可将VIP从原主节点漂移至新主节点,如此便可正常对外提供服务。
7.切换失败回退措施
在极端情况下,若从节点在切换时发生了异常,那么为了尽快恢复业务,需要直接启用原主库。此时,只需要在原主节点上关闭只读模式,便可对前端提供读写服务,命令如下:
mysql> flush table with read lock;
非GTID模式:
mysql> show master status\G;
[..]
File: mysql-binlog.000002
Position: 911094114
[…]
GTID模式:
mysql> show master status\G;
[…]
Executed_Gtid_Set: efb88661-5dba-11ea-b7b5-3a8dd859809f:1-8682
mysql> set global read_only=off;
mysql> set global super_read_only=off;
mysql> unlock tables;
需要注意的是,在非GTID模式下,需要记录回退时的二进制日志文件名和位置;在GTID模式下,需要记录回退时的GTID;以便从库在异常修复之后,可以恢复复制拓扑中的数据同步。