Mysql如何保证高可用

2021-01-04 09:59:55 浏览数 (1)

上一节我们看到主备的一些基本内容,今天我们看看其他的内容

主备延迟

主备切换是很正常的操作,比如服务下线,断电,软件升级等等,首先我们先了解另外一个概念就是同步延迟,与数据同步的三个时间点如下

  1. 主库A上执行的一个事务,写入binlog,这个时刻是T1
  2. 备库B接收完成这个binlog的时刻是T2
  3. 在备库上执行完这个事务时刻就是T3

所谓的主备延迟就是同一个事务在主库和备库上执行完的时间差。

我们可以使用下面命令获取具体的时间差

代码语言:javascript复制
show slave status
seconds_behind_master// 表示备库的延迟时间,表示延迟了多少秒

seconds_behind_master计算方式如下

  1. 每个事物的binlog日志都会有一个时间
  2. 在备库上获取binlog日志的时候,获取到这个时间,计算他和当前系统上的时间差

计算的时间差就是我们的seconds_behind_master,但是我们发现是否会出现主库和备库的系统时间不一致的情况,答案是不可能,因为在备库上获取主库的事务binlog的时候,备库会执行SELECT UNIX_TIMESTAMP()查看备库的时间和主库的时间是否一致,如果不一致,就会在计算seconds_behind_master会扣减这个时间差.

正常情况下,T2-T1的时间差是很小的,因此延迟的时间主要是备库接收完binlog日志之后在在备库上执行完这个事务,很明显时间差的主要来源就是备库上消费中转日志的速度比主库生产binlog日志慢

主备延迟的来源

有些情况是主库部署的机器性能比备库的机器好,比如,有些公司会把主库部署在多个机器上,而把备库部署在一条机器,这样就会导致备库的主机上的多个备库会抢夺资源,导致主备延迟

但是实际上,很少有公司那样做,而是使用的主备可以随时切换,因此部署的机器的性能都一样且是对称部署

但是为什么这样的部署,也会出现主备延迟呢

正常情况下主库设置读写操作,而备库设置只读操作,由于主库的操作可能会影响生产环境的业务,因此大多数消耗资源的操作就会在备库上执行,因此就会导致主备延迟,当然我们也可以使用下面策略

  1. 使用一主多从策略,使用多个从库分担读的能力
  2. 输出binlog日志到外部系统如Hadoop系统,让外部系统提供查询统计这类查询

如果按照一主多从的部署,还会有其他情况引起主备延迟吗

其实大事务也是可能引起延迟的,比如我们在主库上执行了一个大事务,这个事务执行了10分钟,就会导致备库延迟10分钟,因此有时候我们再使用delete删除整张表的数据,就会导致报警,此时我们就必须采用多次删除.

由于存在主备延迟的情况,因此有会有相应的策略

可靠优先策略

上图的双主结构下,状态1到状态2的流程如下

  1. 判断备库上的seconds_behind_master的时间差是否小于某个值(例如5秒),如果不小于,就重复判断直到小于
  2. 把主库设置成只读
  3. 再判断备库上的seconds_behind_master的值为0
  4. 在把备库设置成读写
  5. 把业务切换到备库

这个切换流程,都是在HA系统完成的,这种就是可靠性优先策略

上面SBM就是seconds_behind_master简写

我们发现这种切换会导致系统有不可用时间的,在步骤2之后主备的状态都是只读,这个过程最耗时的就是步骤3,会有几秒的不可用时间,因此就会知道为什么我们需要步骤一,可以尽可能的减少不可用时间,如果没有步骤一,就有可能导致系统的不可用时间很长,一般业务是不可接受的

可用性优先策略

我们是不是可以把步骤4,5提前到最开始,直接切换主备库,这样就不会出现不可用的情况了,但是我们会发现可能会导致数据不一致的情况

我们可以举个例子如下

代码语言:javascript复制

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);

假设主库上的其他表有大量的更新操作,此时的备库延迟是5秒,在主库上执行插入c=4的语句,然后进行主备切换

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

我们来分析一下上面流程

  1. 在步骤2中主库上执行完c=4后,进行主备切换
  2. 步骤3,由于有主备延迟的存在5秒,此时备库还没有进行插入c=4的中转日志,且备库已经开始接受客户端的命令c=5的sql
  3. 在步骤4中,备库中接受了c=5的命令,且生成的binlog传给了主库,且也
  4. 在步骤5中,主库插入了记录(5,5),且在备库上插入了记录(5,4)

我最后发现主备的数据最后两行数据不一致,这是由于可用性优先策略流程导致的

但是此时我们假设binlog_format=row,会记录插入的所有字段,做一只有一行数据不一致,主备库上会发生主键冲突而停止,

在步骤4中,备库的日志记录(4,4),传给主库,而主库的日志记录(4,5)传给备库,都会报错(duplicate key error),因此最后会导致数据一行不一致。

  1. 可以看到如果binlog_format=row是更容易发现,但是如果是mixed或statement就不容易发现,当你发现的时候,已经没有办法查询了,也有可能导致更多的数据不一致
  2. 可用性优先策略可能会导致数据不一致性,因此建议使用可靠性优先策略,数据的一致性比较高.

那些场景可用性优先策略适合呢

比如下面场景就比较适合

  1. 有一个库专门是用来记录操作日志,这个时候数据不一致可以用binlog日志修复,短暂的数据不一致不影响业务流程
  2. 同时,业务系统依赖日志的写入逻辑,如果这个库不可用,会导致业务不可用

那么如果可靠性优先策略异常切换会出现什么效果

但主库断电的时候,这个时候如果是可靠性优先策略,就必须等待seconds_behind_master=0,才能进行切换,此时的系统已经不可用了,因为我们此时的连接还没有到备库上,但是有人说可以直接切到备库,保持备库只读就可以了,实际上这就会导致主库上的中转日志没有完成,直接切换会导致数据丢失的现象,虽然等待中转日志应用完成,数据就会回来,但是短暂的数据丢失也是不能接受的.

我们发现数据库的可用性是依赖主备延迟的时间,延迟时间越少,主备故障时候数据恢复就越小,可用性越高

如果对您有一丝丝帮助,麻烦点个关注,也欢迎转发,谢谢

0 人点赞