MySQL主从复制(二):高可用

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

但是, MySQL要提供高可用能力, 只有最终一致性是不够的。

双M结构的主备切换流程图如下:

主备延迟


与数据同步有关的时间点主要包括以下三个:

1)主库A执行完成一个事务, 写入binlog, 我们把这个时刻记为T1。

2)之后传给备库B, 我们把备库B接收完这个binlog的时刻记为T2。

3)备库B执行完成这个事务, 我们把这个时刻记为T3。

所谓主备延迟, 就是同一个事务, 在备库执行完成的时间和主库执行完成的时间之间的差值, 也就是T3-T1(这个值的时间精度是秒)。

可以在备库上执行show slave status命令, 它的返回结果里面会显示seconds_behind_master, 用于表示当前备库延迟了多少秒。

seconds_behind_master的计算方法是这样的:

1)每个事务的binlog 里面都有一个时间字段, 用于记录主库上写入的时间。

2)备库取出当前正在执行的事务的时间字段的值, 计算它与当前系统时间的差值, 得到seconds_behind_master。

问:如果主备机器的系统时间设置不一致,会不会导致主备延迟的值不准?

答:不会。因为, 备库连接到主库的时候, 会通过执行SELECTUNIX_TIMESTAMP()函数来获得当前主库的系统时间。 如果这时候发现主库的系统时间与自己不一致, 备库在执行seconds_behind_master计算的时候会自动扣掉这个差值。

注:在网络正常的时候, 日志从主库传给备库所需的时间是很短的, 即T2-T1的值是非常小的。 也就是说, 网络正常情况下, 主备延迟的主要来源是备库接收完binlog和执行完这个事务之间的时间差。

主备延迟的来源


场景一:备库机器性能略差

一般情况下,在部署主从时,都会有这么一个想法:反正备库没有请求, 所以可以用差一点儿的机器。 或者, 他们会把20个主库放在4台机器上, 而把备库集中在一台机器上。而且这种部署一般都会将备库设置为"非双一"的模式。

但实际上,备库更新过程中也会触发大量的读操作。所以,当备库主机上的多个备库都在争抢资源的时候,就可能导致主备延迟了。

当然,这种部署现在出现的比较少了。因为主备可能发生切换,备库随时可能变成主库,所以主备库选用相同规格的机器,并做对称部署,这是现在比较常见的情况。

场景二:备库压力大

问1:即便做了对称部署,还是会有可能出现延迟,为什么?

答:备库压力过大。

一般情况下,主库既然提供了写能力,那么备库可以提供一些读能力或者一些运营后台需要的分析语句,。因为不能影响正常业务, 所以只能在备库上跑。

由于主库直接影响业务, 大家使用起来会比较克制, 反而忽视了备库的压力控制。 结果就是, 备库上的查询耗费了大量的CPU资源, 影响了同步速度, 造成主备延迟。

问2:如何解决备库压力大问题?

1)一主多从。 除了备库外, 可以多接几个从库, 让这些从库来分担读的压力。(常用方式)

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

注:备库和从库其实是一个概念,在这里把能够主备切换的称为备库,以和其它从库进行区分。

场景三:大事务

问:采用一主多从,保证备库的压力不会超过主库, 还有什么情况可能导致主备延迟吗?

答:大事务。

因为主库上必须等事务执行完成才会写入binlog, 再传给备库。 所以, 如果一个主库上的语句执行10分钟, 那这个事务很可能就会导致从库延迟10分钟。

这也正是为什么不要一次性地用delete语句删除太多数据,其实这就是一个典型的大事务场景。

比如, 一些归档类的数据, 平时没有注意删除历史数据, 等到空间快满了, 业务开发人员要一次性地删掉大量历史数据。(为了减少大事务导致的主从延迟,需要控制每个事务删除的数据量,分成多次删除)

另一种典型的大事务场景, 就是大表DDL。

场景四:备库的并行复制能力

可靠性优先策略


双M结构的主备切换流程图如下:

主备切换的详细过程如下:

1)判断备库B现在的seconds_behind_master, 如果小于某个值(比如5秒,即保证主从复制在延迟较小的情况下切换) 继续下一步, 否则持续重试这一步。

2)把主库A改成只读状态, 即把readonly设置为true。

3)判断备库B的seconds_behind_master的值, 直到这个值变成0为止。

4)把备库B改成可读写状态, 也就是把readonly设置为false。

5)把业务请求切到备库B。

这个切换流程, 一般是由专门的HA系统来完成的, 我们暂时称之为可靠性优先流程。

可靠性优先主备切换流程:

可以看到, 这个切换流程中是有不可用时间的。 因为在步骤2之后, 主库A和备库B都处于readonly状态, 也就是说这时系统处于不可写状态, 直到步骤5完成后才能恢复。

在这个不可用状态中, 比较耗费时间的是步骤3, 可能需要耗费好几秒的时间。 这也是为什么需要在步骤1先做判断, 确保seconds_behind_master的值足够小。

如果一开始主备延迟就长达30分钟, 而不先做判断直接切换的话, 系统的不可用时间就会长达30分钟, 这种情况一般业务都是不可接受的。

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

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

可用性优先策略


可用性优先策略切换流程:强行把步骤4、 5调整到最开始执行, 也就是说不等主备数据同步, 直接把连接切到备库B, 并且让备库B可以读写, 那么系统几乎就没有不可用时间了。

注:这个切换流程的代价,就是可能出现数据不一致的情况。

下面来看一个可用性优先导致数据不一致的案例,假设有一个表t:

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

接下来, 业务人员要继续在表t上执行两条插入语句的命令, 依次是:

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

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

下图是可用性优先策略,且binlog_format=mixed时的切换流程和数据结果:

切换流程分析:

1)步骤2中, 主库A执行完insert语句, 插入了一行数据(4,4) , 之后开始进行主备切换。

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

3)步骤4中, 备库B插入了一行数据(4,5) , 并且把这个binlog发给主库A。

4)步骤5中, 备库B执行"插入c=4"这个中转日志, 插入了一行数据(5,4) 。 而直接在备库B执行的"插入c=5"这个语句, 传到主库A, 就插入了一行新数据(5,5) 。

最后的结果就是, 主库A和备库B上出现了两行不一致的数据。 可以看到, 这个数据不一致, 是由可用性优先流程导致的。

问1:如果还是使用可用性优先策略,但是设置binlog_format=row, 情况又会怎样呢?

因为row格式在记录binlog的时候, 会记录新插入的行的所有字段值, 所以最后只会有一行不一致。 而且, 两边的主备同步的应用线程会报错duplicate keyerror并停止。 也就是说, 这种情况下, 备库B的(5,4)和主库A的(5,5)这两行数据, 都不会被对方执行。

可用性优先策略,row格式binlog主备切换流程:

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

1)使用row格式的binlog时, 数据不一致的问题更容易被发现。 而使用mixed或者statement格式的binlog时, 数据很可能悄悄地就不一致了。 如果你过了很久才发现数据不一致的问题,很可能这时的数据不一致已经不可查, 或者连带造成了更多的数据逻辑不一致。

2)由于可用性优先策略会导致数据不一致问题,因此在大多数情况下,都建议使用可靠性优先策略。即数据的可靠性要优于可用性。

问2:按照可靠性优先的思路,异常切换会是什么效果?

假设, 主库A和备库B间的主备延迟是30分钟, 这时候主库A掉电了, HA系统要切换B作为主库。 我们在主动切换的时候, 可以等到主备延迟小于5秒的时候再启动切换, 但这时候已经别无选择了。

而如果直接切换到备库B,由于中转日志还没有应用完成,这时客户端查不到之前主库A执行完的事务,会认为有"数据丢失"。

虽然随着中转日志的继续应用, 这些数据会恢复回来, 但是对于一些业务来说, 查询到"暂时丢失数据的状态"也是不能被接受的。

结论:MySQL高可用系统的可用性,是依赖于主备延迟的。延迟的时间越小,在主库故障的时候,服务恢复需要的时间就越短,可用性就越高。

小结:思考题


一般现在的数据库运维系统都有备库延迟监控, 其实就是在备库上执行 show slave status, 采集seconds_behind_master的值。

思考:假设, 现在你看到你维护的一个备库, 它延迟监控的图像类似下图,是一个45°斜向上的线段,你觉得可能是什么原因导致的?又如何去确认这个原因呢?

答:备库的同步在这段时间完全被堵住了。产生这种现象典型的场景主要包括两种:

1)大事务,包括:大表DDL、一个事务操作很多行。

2)从库在进行备份操作,锁住更新。

3)备库磁盘空间不足,无法写入数据。

相关推荐
月光水岸New23 分钟前
Ubuntu 中建的mysql数据库使用Navicat for MySQL连接不上
数据库·mysql·ubuntu
狄加山67524 分钟前
数据库基础1
数据库
我爱松子鱼28 分钟前
mysql之规则优化器RBO
数据库·mysql
chengooooooo1 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
Rverdoser2 小时前
【SQL】多表查询案例
数据库·sql
Galeoto2 小时前
how to export a table in sqlite, and import into another
数据库·sqlite
人间打气筒(Ada)2 小时前
MySQL主从架构
服务器·数据库·mysql
leegong231112 小时前
学习PostgreSQL专家认证
数据库·学习·postgresql
喝醉酒的小白2 小时前
PostgreSQL:更新字段慢
数据库·postgresql
敲敲敲-敲代码2 小时前
【SQL实验】触发器
数据库·笔记·sql