第三十二节、主库出问题了,从库怎么办?
一主多重的切换正确性:
1、基于位点的主备切换
通常情况下,在切换任务的时候,要先主动跳过这些错误,有两种常用的方法:
1、主动跳过一个事物,跳过的命令的写法是:
set global sql_slave_skip_counter=1;
start slave;
因为切换过程中,可能会不止重复执行一个事物,所以需要在从库持续观察,每次碰到这些错误就停下来,执行一次跳过命令,直到不再出现停下来的情况,以此来跳过可能涉及的所有事务。
2、通过设置slave_skip_errors参数,直接设置跳过指定的错误:
在执行主备切换时,有这么两类错误,是经常会遇到的:
1062 错误是插入数据时唯一键冲突;
1032错误是删除数据时找不到行。
因此,可以把slave_skip_errors设置为“1032,1062”,这样中间碰到这两个错误时就直接跳过
2、GTID
通过sql_slave_skip_counter跳过事务和通过slave_skip_errors 忽略错误的方法,虽然都最终可以建立从库B和主库A'的主备关系,但这两种操作都很复杂,而且容易出错,所以MySQL5.6版本引入了GTID,彻底解决了这个困难。
GTID的全程是Global Transaction Identifier,也就是全局事务ID,是一个事务在提交的时候生成的,是这个事务的唯一标识。它由两部分组成,格式是:GTID=server_uuid:gno.
其中:server_uuid是一个实例第一次启动时自动生成的,是一个全局唯一的值;
gno是一个整数,初始值是1,每次提交事务的时候分配给这个事务,并加1.
在MySQL的官方文档里,GTID格式是这么定义的:
GTID=source_id:transaction_id
GTID模式的启动:在启动一个MySQL实例的时候,加上参数gtid_mode=on 和enforce_gtid_consistency=on。
在GTID模式下,每个事务都会跟一个GTID——对应。这个GTID有两种生成方式,而使用哪种方式取决于session变量gtid_next的值。
1、如果gtid_next=automatic,代表使用默认值。这时,MySQL就会把server_uuid:gno分配给这个事务 。
a、记录binlog的时候,先记录一行 SET @@SESSION.GTID_NEXT='server_uuid:gno';
b、把这个GTID加入本实例的GTID集合。
2、 如果gtid_next是一个指定的GTID的值,比如通过set gtid_next='current_gtid'指定为current_gtid,那么就有两种可能:
a、如果current_gtid已经存在于实例的GTID集合中,接下来执行的这个事务会直接被系统忽略;
b、如果current_gtid没有存在于实例的GTID集合中,就将这个current_gtid分配给接下来要执行的事务,也就是说系统不需要给这个事务生成新的GTID,因此gno也不用加1
GTID实例:
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t values(1,1);
假设,现在这个实例X是另外一个实例Y的从库,并且此时在实例Y上执行了下面这条语句:
insert into t values(1,1);
并且,这条语句在实例Y上的GTID是: aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10.
那么,实例X作为Y的从库,就要同步这个事务过来执行,显然会出现主键冲突,导致实例X的同步线程停止。
处理方法就是,可以执行下面的这个语句序列:
set gtid_next='aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10';
begin; commit;
set gtid_next=automatic;
start slave;
其中,前三条语句的作用,是通过提交一个空事务,把这个GTID加到实例X的GTID集合中。这样,再执行start slave命令让同步县城执行起来的时候,虽然实例X上还是会继续执行实例Y传过来的事务,但是由于 'aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10'已经存在于实例X的GTID集合中了,所以实例X就会直接跳过这个事务,就不会再出现主键冲突的错误。
在上面的这个语句序列中,start slave命令之前还有一句 set gtid_next=automatic。这句话的作用是“恢复GTID的默认分配行为”,也就是说如果之后有新的事务再执行,就还是按照原来的分配方式,继续分配gno=3.