MySQL 25 MySQL是怎么保证高可用的?

正常情况下,只要主库执行更新生成的所有binlog,都可以被传到备库并被正确地执行,备库就能达到跟主库一致的状态,这就是最终一致性,而MySQL要提供的高可用能力,只有最终一致性是不够的。

主备延迟

主备切换可能是一个主动运维动作,比如软件升级、主库所在机器按计划下线等,也可能是被动操作,比如主库所在机器掉电。

先看主动切换的场景。首先介绍同步延迟的概念,与数据同步有关的时间点主要包括三个:

  • 主库A执行完成一个事务写入binlog,将这个时刻记为T1;

  • 之后传给备库B,把备库B接收完这个binlog的时刻记为T2;

  • 备库B执行完这个事务,将这个时刻记为T3。

主备延迟指的是,同一个事务主备库执行完成的时间差,即T3-T1。

可以在备库上执行show slave status命令,其返回结果里会显示seconds_behind_master,这个值计算的就是T3-T1。

如果主备库机器的系统时间设置不一致,会不会导致主备延迟的值不准呢?答案是不会的,备库连接到主库的时候,会通过执行SELECT UXIN_TIMESTAMP()函数来获得当前主库的系统时间,如果此时发现两库系统时间不一致,备库执行seconds_behind_master计算时会扣除该差值。

网络正常时,T2-T1是很短的,主备延迟的主要来源是备库接收完binlog和执行完这个事务之间的时间差。所以,主备延迟最直接的表现是,备库消费中转日志(relay log)的速度,比主库生产binlog的速度要慢。

主备延迟的来源

首先,有些部署条件下备库所在机器性能要比主库所在的机器性能差。

有些人部署时想着反正备库没有请求,可以用差一点的机器,但实际上更新过程也会触发大量读操作,当备库主机上的多个备库都在争抢资源时,就可能导致主备延迟。

现在这种部署比较少了,因为主备可能随时切换,所以主备库一般选用相同规格的机器,并且做对称部署。

第二,备库的压力大。

备库一般用来提供一些读能力,如果大量查询,备库上的查询可能耗费大量CPU资源,影响同步速度,造成主备延迟。

这种情况一般的处理方法:

  • 一主多从,让多个从库分担读的压力;

  • 通过binlog输出到外部系统,比如Hadoop,让外部系统提供统计类查询能力。

第三是大事务。

主库上必须等事务执行完才会写入binlog再传给备库。那么,如果一个主库上的语句执行10分钟,意味着这个事务可能会导致从库延迟10分钟。常见的大事务有一次性用delete删除大量数据、大表DDL等。

第四是备库的并行复制能力,这个下篇文章再介绍。

由于主备延迟的存在,所以在主备切换的时候,就相应的有不同的策略。

可靠性优先策略

上篇文章提到的双M结构:

双M结构下,状态1切换到状态2的详细过程:

  • 判断备库B现在的seconds_behind_master,如果小于某个值(如5秒)继续下一步,否则持续重试这一步;

  • 将主库A改成只读状态;

  • 判断备库B的seconds_behind_master,直到这个值变成0;

  • 将备库B改成可读写状态;

  • 把业务请求切换到备库B。

我们暂时把这个切换流程称为可靠性优先流程。

可以发现,这个切换流程中,在步骤2之后,主备库都处于readonly状态,即系统处于不可写状态,直到步骤5完成后才能恢复。在这个不可用状态中,步骤3比较耗时,这也是为什么在步骤1先确保seconds_behind_master足够小,不然不可用时间可能会很长。

系统的不可用时间,是由数据可靠性优先的策略决定的。也可以选择可用性优先的策略,把这个不可用的时间几乎降为0。

可用性优先策略

如果强行把步骤45调整到最开始执行,即不等主备数据同步,直接把连接切到备库B且让备库B可以读写,那么系统几乎就没有不可用时间了。

暂时把这个切换流程称为可用性优先流程,该切换流程的代价是可能出现数据不一致的情况。

假设有一个表t:

sql 复制代码
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);

初始化数据后,主备库上都是3行数据,继续插入:

sql 复制代码
insert into t(c) values(4);
insert into t(c) values(5);

假设现在主库上其他的数据表有大量的更新,导致主备延迟达到5秒,在插入一条c=4的语句后,发起了主备切换。

当使用可用性优先策略,且binlog_format=mixed时切换流程如下:

图中的SBM,是seconds_behind_master参数的简写。

  • step 2中,主库A执行完insert插入(4,4),之后开始进行主备切换;

  • step 3中,由于主备之间有5秒延迟,所以备库B还没来得及应用插入c=4中转日志,就开始接收客户端插入c=5的命令;

  • step 4中,备库B插入一行数据(4,5),并把这个binlog发给主库A;

  • step 5中,备库B执行插入c=4的日志,插入一行数据(5,4),而前面的语句传到主库A,会在主库插入(5,5)。

因此最后出现主备不一致的情况。

当使用可用性优先策略,且binlog_format=row时切换流程如下:

由于row格式在记录binlog时会记录新插入的行的所有字段值,所以最后只会有一行不一致,且两边的主备同步的应用线程会报错duplicate key error并停止。即备库B的(5,4)和主库A的(5,5)两行数据都不会被对方执行。

从上面的分析中,可以看到一些结论:

  • 使用row格式的binlog时,数据不一致问题更容易被发现,而使用mixed或者statement格式的binlog,可能数据不一致而没有发现;

  • 主备切换的可用性优先策略会导致数据不一致,因此大多数情况都建议使用可靠性优先策略

也有情况数据的可用性优先级更高。比如一个库的作用是记录操作日志,这时如果数据不一致可以通过binlog来修补,短暂的不一致不会引发业务问题,而业务系统又依赖这个日志,如果这个库不可写会导致线上的业务操作无法执行。这时就需要先强行切换,事后再补数据的策略。

当然也能改进,比如让业务逻辑不要依赖于这类日志的写入,那就又可以使用可靠性优先策略。

最后总结:在满足数据可靠性的前提下,MySQL高可用系统的可用性,是依赖于主备延迟的。延迟的时间越小,在主库故障的时候,服务恢复需要的时间就越短,可用性就越高。

相关推荐
tq10862 小时前
MySQL definer does not exist 问题分析
mysql
惊骇世俗王某人9 小时前
MySQL数据库索引及底层数据结构
数据结构·数据库·mysql
神仙别闹16 小时前
基于Python+Vue+Mysql实现(物联网)智能大棚
vue.js·物联网·mysql
花途Jasmine17 小时前
MySQL中的DDL(一)
数据库·mysql
五岁小孩吖1 天前
Mysql 如何使用 binlog 日志回滚操作失误的数据
mysql
Runing_WoNiu1 天前
最佳左前缀法则(Optimal Left-Prefix Rule)
数据库·mysql
百里晴鸢1 天前
mysql之主从复制
mysql
五月茶1 天前
MySQL——黑马
数据库·mysql
一号IT男1 天前
MySql MVCC的原理总结
数据库·mysql