image.png
A与A’为互备,BCD是A的从库。A故障时,主备切换后。
image.png
对B、C、D来说,需要切换master到A’,切换主节点的命令change master

  1. CHANGE MASTER TO
  2. MASTER_HOST=$host_name
  3. MASTER_PORT=$port
  4. MASTER_USER=$user_name
  5. MASTER_PASSWORD=$password
  6. MASTER_LOG_FILE=$master_log_name
  7. MASTER_LOG_POS=$master_log_pos

前四个参数分别代表:ip、端口、用户名、密码。mater_log_name同步日志文件名,master_log_pos,日志偏移量。
但是,虽然A和A’互为主备,但是相同的日志在A的位点和A’的位点可能是不同的。这时B切换master到A’,无法精准找到同步位点,考虑到切换不能导致丢数据,所以MySQL采用的是找一个稍微往前的位点,开始切换后同步,然后再通过判断跳过那些在从库B上已经执行过的事务。
对于已执行过的事务再次被同步执行时,肯定会报主键冲突。那么,判断并跳过的方式:
1)每次遇到报错,手动跳过1个事务
set global sql_slave_skip_counter=1;
start slave;
2)直接跳过执行类型的报错
将slave_skip_errors设置为‘1032,1062’,1032代表的错误是删除数据时找不到行记录,1062代表的错误是插入数据时唯一键冲突。

GTID协议

切换主从时,手动跳过单个事务、或者跳过指定类型报错,实际操作起来都很复杂。MySQL在5.6版本后引入了GTID,也就是global transaction identifier,全局事务id,是一个事务的唯一标识。格式是:
GTID=server_uuid:gno(GTID=source_id:transaction_id)
其中,server_uuid是一个示例第一次启动时自动生成,全局唯一
gno是一个整数,初始值是1,每次提交事务时加1。由于是事务提交时才会分配gno并+1,不会有回滚,所以gno是递增且连续的。

开启GTID

gtid_mode=on
enforce_gtid_consistency=on

GTID生成方式

gtid_next=automatic,默认自动生成一个server_uuid:gno给这个事务。
gtid_next=指定值。
每个MySQL实例都会维护一个GTID集合,来代表自己已经执行过的所有事务。对于GTID相同的事务,MySQL会自动判断跳过,避免重复记录和主键冲突。

基于GTID的主从切换

  1. CHANGE MASTER TO
  2. MASTER_HOST=$host_name
  3. MASTER_PORT=$port
  4. MASTER_USER=$user_name
  5. MASTER_PASSWORD=$password
  6. master_auto_position=1

其中参数master_auto_position=1表示主备关系使用的GTID协议,这样就不用指定同步文件名和同步位点了。
我们把现在这个时刻,实例 A’的 GTID 集合记为 set_a,实例 B 的 GTID 集合记为 set_b。接下来,我们就看看现在的主备切换逻辑。
我们在实例 B 上执行 start slave 命令,取 binlog 的逻辑是这样的:
1)实例 B 指定主库 A’,基于主备协议建立连接。
2)实例 B 把 set_b 发给主库 A’。
3)实例 A’算出 set_a 与 set_b 的差集,也就是所有存在于 set_a,但是不存在于 set_b 的 GTID 的集合,判断 A’本地是否包含了这个差集需要的所有 binlog 事务。
a. 如果不包含,表示 A’已经把实例 B 需要的 binlog 给删掉了,直接返回错误;
b. 如果确认全部包含,A’从自己的 binlog 文件里面,找出第一个不在 set_b 的事务,发给 B;
4)之后就从这个事务开始,往后读文件,按顺序取 binlog 发给 B 去执行。