MySQL:从 MySQL 看主从架构高可用性实现
从进入互联网时代开始,我们从单机走向集群再到当前的微服务架构,我们已经很少再使用单机架构来实现业务逻辑,即使没有使用微服务,但是主备、主从等集群已经属于是业务侧必备能力。
但是,无论是主备还是主从架构,实际上就是为了系统的高可用性实现的一个策略,防止主机因为某些故障导致异常下线,这时候备份或者从实例就会通过选择或者其他策略成为主服务实例,对外继续提供服务。
在 MySQL 的正常情况下,只要主库执行更新生成的所有 binlog 全部被正确的传到备库并且被正确执行,备库就能和主库数据一致,实现最终一致性。但是最终一致性并不能满足线上的性能需求,还需要保证集群的可用性。
1 主备延迟
1.1 主备延迟
在发生主备延迟时,与数据同步的时间点主要包括:
主库 A 执行完成一个事务,写入 binlog,我们把这个时刻记为 T1;
之后传给备库 B,我们把备库 B 接收完这个 binlog 的时刻记为 T2;
备库 B 执行完成这个事务,我们把这个时刻记为 T3。
主备延迟,就是同一个事务,在备库执行完成的时间和主库执行完成的时间之间的差值,就是 T3-T1。
在备库执行 show slave status 会得到 seconds_behind_master,表示备库延迟的时间,计算方法为:
每个事务的 binlog 都有一个时间字段,用于记录主库写入时间;
备库取出当前正在执行的事务的时间字段的值,计算与当前系统时间差值,就是该值,单位为秒;
如果主备库机器的系统时间设置不一致,不会导致主备延迟的值不准。因为,备库连接到主库的时候,会通过执行 SELECT UNIX_TIMESTAMP() 函数来获得当前主库的系统时间。如果这时候发现主库的系统时间与自己不一致,备库在执行 seconds_behind_master 计算的时候会自动扣掉这个差值。
但是:如果备库已经连接主库后,修改主库的系统时间,备库同步的时候就不会再做时间的自动修正了,因此,时间修正只有第一次建连的时候才会执行。
在网络正常的时候,日志从主库传给备库所需的时间是很短的,即 T2-T1 的值是非常小的。也就是说,网络正常情况下,主备延迟的主要来源是备库接收完 binlog 和执行完这个事务之间的时间差。所以说,主备延迟最直接的表现是,备库消费中转日志(relay log)的速度,比主库生产 binlog 的速度要慢。
1.2 主备延迟的来源
1.2.1 主备机性能有差距
备库所在机器性能比主库的机器性能差,此时一般将备库设置为“非双 1”模式【牺牲备库的一点可靠性,减少写盘次数,增强 IO 能力】,更新过程中触发大量读操作,可能会导致主备延迟。
现在这种情况比较少,因为现在都是主从部署,可能随时发生主从切换,因此一般都是对称部署。
1.2.2 备库压力大
一般出现的原因是读写分离场景,备库对外提供读能力,查询耗费大量 CPU 资源,影响了同步速度,造成主备延迟。
此时的处理措施是:
一主多从,用从库分担压力;
通过 binlog 输出到外部系统,比如 Hadoop 系统,提供统计类查询能力;
从库和备库在概念上其实差不多。在我们这个专栏里,为了方便描述,我把会在 HA 过程中被选成新主库的,称为备库,其他的称为从库。
1.2.3 大事务
主库必须等事务执行完成后才能写入 binlog,再传给备库,造成主备延迟。
比如说大量数据的删除就会造成大事务,一般是要求分批执行。之所以删除会造成大事务,是因为无论是否有索引,存储引擎都是一条条数据查询并加锁,返回给执行引擎,执行引擎标记数据删除。所有的数据都处理完成后,才会提交事务释放锁。
另一种就是大表 DDL。
1.3 主备延迟的排查思路
1)查数据库在干什么
2)查看 sql_thread 在干什么
slave 上查看状态:
查看 relay_master_log_file 以及 exec_master_log_pos
master 上解析 binglog 日志:
如果发现卡在操作某表上:
1)检查表结构
没有索引:stop slave 可能会卡主,建议关闭 mysql,启动后先加索引,然后 start slave
有索引:只能等,大事务需要做拆分,不要操作太多数据
2)大事务:M 上 session 回话使用 statement 格式,使用语句级别的复制
3)查看 MySQL 状态
机器性能(CPU、IO 等):从库配置适当高一点,使用新硬件 PCI-E 或 SSD 设备
表结构: 设计要合理,必须有主键,主键要短小,为查询字段建索引
业务程序:适当使用缓存,减少数据库压力
分析 MySQL 进程并结合源码:
4)参数临时优化
主库开启 group commit
从库开启 writeset
从库设置 sync_binlog=0 && innodb_flush_log_at_trx_commit=2
5)检查锁情况
2 主备切换策略
2.1 可靠性优先策略
在双 M 结构下,主备切换的流程如图:
判断备库 B 现在的 seconds_behind_master(SBM),如果小于某个值(比如 5 秒)继续下一步,否则持续重试这一步;这里主从延迟时间短,说明当前没有大事务,延迟比较低,减少因为延迟造成数据不可靠的几率;
把主库 A 改成只读状态,即把 readonly 设置为 true;
判断备库 B 的 seconds_behind_master 的值,直到这个值变成 0 为止;
把备库 B 改成可读写状态,也就是把 readonly 设置为 false;
把业务请求切到备库 B。
这个切换流程,一般是由专门的 HA 系统来完成的,我们暂时称之为可靠性优先流程。
这个切换流程中是有不可用时间的。因为在步骤 2 之后,主库 A 和备库 B 都处于 readonly 状态,也就是说这时系统处于不可写状态,直到步骤 5 完成后才能恢复。
在这个不可用状态中,比较耗费时间的是步骤 3,可能需要耗费好几秒的时间。这也是为什么需要在步骤 1 先做判断,确保 seconds_behind_master 的值足够小。
2.2 可用性优先策略
如果是直接将第 4 和第 5 步提前,保证了系统几乎么有不可用时间,但是可能造成数据不一致。
其实这就是 CAP 中的 C 和 A,MySQL 主库在写完 binlog 后就给客户端响应了,没等 binlog 同步到一个或多个备库,这种策略是在 C 和 A 之间选择了 A,牺牲了 C,如果主库宕机了,但 binlog 的最后一个或几个事务没同步到备库,那备库成为主库后,数据就丢了。其它的 NoSQL 很多是给用户提供了选择,比如 Mongo,用户可以设置日志同步到几个 Slave 后再给客户端响应,同步的 Slave 越多,C 越强,A 越弱,比如同步到 X 个 Slave 后再给客户端响应,那即使任何 X 个节点宕机,集群中仍然有 1 个节点有最新日志,它会成为主节点,数据没丢,集群还可以工作。
在满足数据可靠性的前提下,MySQL 高可用系统的可用性,是依赖于主备延迟的。延迟的时间越小,在主库故障的时候,服务恢复需要的时间就越短,可用性就越高。
2.3 常见切换技术
semi-sync 在网络故障超时的情况下会退化成 async,这个时候如果刚好主库掉电了,有些 binlog 还没有传给从库,从库无法判断数据跟主库是否一致,如果强行切换可能会导致丢数据,在金融业务场景下只能"人工智能"来做切换,服务中断时间长。AliSQL 采用双通道复制更容易判断主备数据是否一致,如果一致可以自动切换,如果不一致才需要人工恢复数据。
版权声明: 本文为 InfoQ 作者【互联网工科生】的原创文章。
原文链接:【http://xie.infoq.cn/article/512d3c1c053d9d7bdcdf48989】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论