正常情况下,只要主库执行更新生成的所有binlog,都可以传到备库并被正确执行,备库就能达到跟主库一样的状态,这就是最终一致性。
这里我们再放上mysql主备切换的流程图:
主备切换可能是一个主动的运维动作,如软件升级等。也可能是被动操作,如主库所在机器掉电等。
主备同步的过程通常有以下三步:
- 主库a执行完成一个事务,写入binlog,我们把这个时刻记为t1;
- 之后将主库binlog传给备库,备库b接收完这个binlog的时刻记为t2;
- 备库b执行完这个事务,我们把这个时刻记为t3;
所谓主备延迟,就是同一个事务,在备库执行完成的时间和主库执行完成的事件之间的差值,也就是t3-t1。
我们可以在备库上执行show slave status命令,会返回seconds_bebind_master,用于表示当前备库延迟了多少秒。
实际上,在网络正常的时候,日志从主库传递给备库的时间是很短的。主要延迟的时间是在备库消费中转日志(relay log)速度,这个速度是要比主库生产binlog的速度要慢的。
第一种可能,有些部署条件下,备库所在机器的性能要比在主库所在的机器性能差。
因为习惯性认为备库的请求不好,没必要用那么好的机器。但是在发生主备切换的时候就比较麻烦。
第二种可能,备库的压力比较大。
因为大家比较重视主库,可能就会忽略备库的压力控制。备库上的查询可能耗费了大量的cpu资源,影响了同步速度,造成主备延迟。
解决办法可以采用一主多从的架构,让多个从库来分担压力。
第三种可能,大事务。
大事务很容易理解,因为主库上必须等待事务执行完成才会写入binlog,再传给备库。所以,如果一个事务在主库上面执行10分钟,那这个也会导致从库上延迟10分钟。
解决办法就是尽量避免一个事务执行过长的时间。
可靠性优先策略的主备切换流程主要有以下几步:
- 判断备库b现在的second_behind_master,如果小于某个值(例如5秒)继续下一步,否则持续重试第一步;
- 把主库a改成只读状态,即把readonly设置为true;
- 判断备库b的second_behind_master的值,直到这个值变成为0;
- 把备库b改成可读可写状态,也就是把readonly设置为false;
- 把业务请求切换到备库b;
具体流程如下图所示(sbm代表seconds_bebind_master):
可以看到,这个切换流程中是有不可用时间的。因为在步骤2之后,主库a和备库b都处于readonly状态,也就是说系统处于不可写状态,直到步骤5才恢复。正是因为这个原因,所以在第一步菜肴确保主从延迟的时间尽可能短。
因为可靠性优先策略存在不可用时间,所以出现了可用性优先策略,这个方法几乎可以将不可用时间降为0。
可用性优先策略,就是把步骤4、5调整到最开始执行。也就是说不等主备数据同步,直接连接切到备库b,且让备库b可以读写。
但是这种可用性优先策略可能会存在主备不一致的情况,如下的例子,我们先建立下面这张表:
mysql> create table t(
id int(11) unsigned not null auto_increment,
c int(11) unsigned default null,
primary key(id)
)engine=innodb;
insert into t(c) values(1),(2),(3)
接下来要进行的业务操作是要继续在这张表上插入两条数据:
insert into t(c) values(4);
insert into t(c) values(5);
假设,主库上其他的数据表有大量的更新,导致主备延迟5秒,在插入一条c=4的语句后,进行主备切换。
在使用可用性优先策略,且binlog_format=mixed是进行主备切换,过程如下图:
我们分析下上面的流程:
- 步骤1中,主备延迟5秒;
- 步骤2中,向库a插入记录(4,4);
- 步骤3中,发生主备切换,向库b插入记录(4,5)
- 步骤4中,因为库b发生更新,也要同步binlog到库a
- 步骤5中,库a和库b分别执行relay log。库a插入记录(5,5),库b插入记录(5,4)
从上面的步骤执行完的结果来看,库a和库b出现了不一致。这是因为可用性优先策略流程导致的。这样就就会有两行数据不一致。
那如果设置binlog_format=row,会发生什么情况呢?如下图所示:
我们分析下上面的流程:
- 步骤1中,主备延迟5秒;
- 步骤2中,向库a插入记录(4,4);
- 步骤3中,发生主备切换,向库b插入记录(4,5)
- 步骤4中,因为库b发生更新,也要同步binlog到库a
- 步骤5中,库a和库b分别执行relay log,a库试图插入(4,5),b库试图插入(4,4)。
因为binlog_format=row会记录插入行的具体值,所以最后进行主备同步的时候,会报错duplicate key error。这样就就会有一行数据不一致。
综上,我们可以得到以下结论:
- 使用row格式的binlog,数据不一致问题更容易被发现。而使用mixed或者statement的binlog,数据可能悄悄就不一致了。
- 主备切换的可用性优先策略会导致数据不一致。因此,大多数情况下建议使用可靠性优先策略。