为什么要做 MySQL 切主?
高可用性(high availability,缩写为 HA),指系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。
高可用性通常通过提高系统的容错能力来实现。定义一个系统怎样才算具有高可用性往往需要根据每一个案例的具体情况来具体分析。
计算公式为:
A(可用性),MTBF(平均故障间隔),MTTR(平均修复时间)
MTTR**,**平均修复时间(Mean Time To Repair)。 即出现故障后修复故障的平均时间。 MTTR 越小,表示故障时间越短,可用性也就越高。
MySQL 复制
MySQL 异步复制
主库 master 把 binlog 日志推送给从库,主库不需要等到从库成功把数据更新到 relay log,主库就直接提交事务即可。这种性能最佳,但是牺牲了主从数据一致性。
MySQL 半同步复制
简介:
主库 master 把 binlog 日志推送给从库,主库需要等到从库成功拿到了事务的 binlog,即当收到从库的 ACK 命令之后才 commit 事务。
关键点:
-
master 节点 commit 意味着 salve 获取到了 binlog,保证了 master 宕机不丢事务;
-
但是,仅仅是 binlog 同步到 slave,并不能保证 slave 成功写入该 binlog(只同步了一半)。
可能存在的问题:
Engine Commit 可以理解为是否真正写入到数据表,此时查询主库是可以查询到该条数据。
User1 查询一条数据 3,但是 master 一直在等待 slave 的 ack;此时 User2 又有一笔查询,因为 after commit 的模式,可以查询到该 3 这条数据。假若 master 此刻宕机,备机并没有成功收到该条 binlog;然后,再切主到备机时,这条 insert 的 binlog 就丢失了,User2 再来查询时会发现没有数据 3,产生了幻读。
MySQL 增强半同步复制
可能存在的问题:卡主情况下查询不到数据,业务重试导致重复写入(表无唯一键情况)。
Q:使用增强半同步,理论上也不能实现数据完全一致吧?如果对于特别重要的数据,字节内部如何确保主库故障之后快速切换到从库,有哪些校验机制呢?
A: 不能保证强一致读,但是可以保证 ACK 后的从节点,数据不会丢失,只是需要一点时间就可以读到。在切换前会通过 show slave status 命令查看各个 slave 的应用 master 的** exec_master_log_pos **位点,并找应用位点最新的作为候选主库;在老主库可以连接的情况下,看候选主库应用的 binlog 是否与老主库的 binlog 相同,如果相同则切换;在老主库不能连接时,则直接切换。
Q:半同步复制的架构,如果主挂了,那切到从机房时具体的操作步骤是怎样的?检查数据是否已经完成重放如何做的呢?
A:
选取同步数据最多的节点作为新主,同步有 binlog 位置作为判断同步数据的位点
其余节点挂复制到新主节点同步数据
修改代理配置指向新主节点
MySQL组复制(Group Replication)
MySQL 5.7引入了组复制 MGR 组件,使用分布式数据一致性协议 Paxos 保证数据一致性;本质上是引入 Paxos 来保证其它节点收到 binlog;
事务提交时用 MGR 将事务信息广播到各个节点,过半数的节点确认后才可以提交成功;节点同步数据还是通过回放 binlog。
MySQL切主过程
柔切(可靠性优先)
状态 1 到状态 2 的可靠性优先的过程:
判断备库 B 现在的 seconds_behind_master,如果小于某个值(比如 5 秒)继续下一步,否则持续重试这一步;
把主库 A 改成只读状态,即把 readonly 设置为 true;
判断备库 B 的 seconds_behind_master 的值,直到这个值变成 0 为止;
把备库 B 改成可读写状态,也就是把 readonly 设置为 false;
把业务请求切到备库 B。
这个切换流程是由专门的 HA 系统来完成的,我们可以称之为可靠性优先流程。
seconds_behind_master 的计算方法:
每个事务的 binlog 里面都有一个时间字段,用于记录主库上写入的时间;
备库取出当前正在执行的事务的时间字段的值,计算它与当前系统时间的差值,得到 seconds_behind_master。
强切(可用性优先)
状态 1 到状态 2 的可用性优先的过程
切换前会通过 show slave status 命令查看各个 slave 的应用 master 的exec_master_log_pos位点;
找应用位点最新的作为候选主库,并设置为 Readonly;
把备库 B 改成可写状态,即 readonly 设置为 false;
把业务请求切到备库 B。
一些思考
GTID 的全称是 Global Transaction Identifier,也就是全局事务 ID,是一个事务在提交的时候生成的,是这个事务的唯一标识。它由两部分组成,格式是:source_id:transaction_id
为什么我们强切后进行数据修复完,老的主库要被隔离,不能直接把老的主库挂成从库;如果想把老的主库做成从库,需要基于现有的主库重做数据。
参考
MySQL复制的前世今生
bytetech.info/videos/set/…