MySQL 基础技术(四)—— MySQL 如何保证高可用?
之前,有一年多的工作客户端领域的工作经验。
后来,也在字节做了一年多的后端业务。
现在希望做一些MySQL
总结,丰富一下自己在后端领域的积累。
目录如下:
MySQL 基础技术(一) —— MySQL 是如何查询的?
MySQL 基础技术(二) —— MySQL 是如何更新的?
MySQL 基础技术(三)—— MySQL 如何保证数据不丢失?
MySQL 基础技术(四)—— MySQL 如何保证高可用?
一、引子
上一篇文章,我们讲述了:《MySQL 如何保证数据不丢失?》,介绍了 binlog
和 redo log
的工作流程。
那么,MySQL
怎么保证高可用呢?
为了提高 MySQL
的读写性能,我们往往采用 MySQL
一主多从的方案。
即一个主库(主要负责写),多个从库(只负责读)。
因为单实例有性能瓶颈,多从库能优先解决 MySQL
的读负载压力。
二、主从同步
![](https://www.lmdhx.com/wp-content/uploads/2022/07/frc-15c298bb30e5943058c9c2d4437d1e44.jpg)
原理:
将 MySQL
设计成一主多从模式。
简单来说,主要分为三步:
- 第一步:所有增删改的
DML
语句都在master
节点的示例上完成。 - 第二步:将处理完成的
binlog
日志传输到各个slave
节点。 - 第三步:多个
slave
节点处理binlog
,从而保持主从一致。
详细来说,
![](https://www.lmdhx.com/wp-content/uploads/2022/07/frc-25eef1b692433defbab3720407eac0a8.jpg)
Master
与 Slave
之间会维护一个长连接,专门用来同步binlog
。
创建从库的过程:
- 在
Slave
机器上,通过change master
命令,设置主库的 IP、端口号、用户名、密码,以及binlog
从哪里开始获取等信息(具体binlog
文件名 + 文件偏移量)。 - 在
Slave
机器上,执行start slave
命令,启动io_thread
和sql_thread
线程。
其中io_thread
用于接收主库的binlog
,sql_thread
用于处理主库的binlog
。 Slave
开始尝试连接Master
,Master
校验完用户名密码后,dump_thread
根据Slave
设置的binlog
文件和偏移量,开始读取binlog
发送给Slave
。Slave
的io_thread
将接收到的binlog
写到relay log
(中转日志)。sql_thread
读取中转日志,执行对应SQL,同步完成。
问题:
1. 主从延迟
即“同步延迟”。
表示同一个事务下,主库执行完成到备库执行完成的时间差值。
![](https://www.lmdhx.com/wp-content/uploads/2022/07/frc-c1630d6dee93e8fbb39141d9e7ff4a18.jpg)
时间线:
Master
执行一个事务,成功写入binlog
—— 这个时刻,我们记为 T1。Slave
的io_thread
接收到binlog
—— 这个时刻,我们记为 T2。Slave
执行完这个事务。—— 这个时刻,我们记为 T3。
所谓主从延迟,就是 T3-T1 的时间。
如果在这段时间里,在从库上查询主库刚插入/修改的数据,会出现主从不一致的现象。
这时,一些对可靠性要求比较高的业务场景里,就会出现错误。
我们可以在从库上执行:
show slave status;
其中,seconds_behind_master
就是从库延迟的时间(T3-T1)
主从延迟的根本原因是:从库消费中转日志(
relay log
)的速度比主库生产binlog
的速度慢。
2. 主从切换
在实际场景下,可能会遇到主库所在机器异常、掉电、或者机房升级等等。
这就会涉及到“主库”与“从库”之间的切换问题。
由于主从延迟的存在,在主从切换的时候,就会有不同的策略。
![](https://www.lmdhx.com/wp-content/uploads/2022/07/frc-fcc6c3bdfc6f00b0df87d720f00c35a9.jpg)
可靠性优先策略(推荐):
- 查询
slave
的seconds_behind_master
,如果小于预定的某个值(比如3秒),就下一步。
否则就一直轮训,直到出现满足条件的Slave
。(选未来主库) - 将
master
的readonly = true
,降为从库。 - 查询该
slave
(未来主库) 的seconds_behind_master
值变成 0。(即无主从延迟) - 将该
slave
(未来主库)的状态变成读写。readonly = false
,升成主库。 - 将请求流量切到新主库。
- 优点:可靠性高,数据可靠。
- 缺点:会有一小段不可用的时间。
因此,得选择
seconds_behond_master
比较短的slave
升master
。
可用性优先策略:
- 直接将
slave
(未来主库)的状态变成读写。readonly = false
,升成主库。 - 将请求流量切到新主库。
- 将老主库的
readonly = true
,降为从库。
- 优点:可用性高,没有真空期。
- 缺点:可能会出现数据不一致的情况。
三、如何保证高可用
MySQL 如果要保证高可用,就要满足三个条件。
- 数据不丢失。(双1策略)
- 主从最终一致性。(主库所有binlog,备库都执行了)
- 无主从延迟。
主从延迟的来源:
1. Slave
所在机器性能问题。(部署在同一机器上)
我就遇到过这种 case:
我们的数据库和飞书的数据库部署在同一个机器上,
他们在大量的做一些DML操作,删除/归档很多老数据。
导致于我们的Slave资源被一直抢占,进而出现主从延迟。
解决思路:
- 如果成本允许,按服务,分开独立部署。
2. Slave 压力大,查询耗费了大量CPU资源,影响了同步速度。
这种也比较常见,表/索引设计不合理、或者有临时任务在拖库,导致慢慢查询,耗费了大量CPU
资源。导致 io_thread
、sql_thread
抢占不到资源进而同步缓慢。
解决思路:
1.优化表设计、索引设计。解决慢 SQL 问题。
2.增加从库,分担现有从库的压力。
3.对于一些临时/定时任务:可用 Binlog -> Hadoop。转移让另外一个系统来提供查询能力。
3. 大事务
这种也比较好理解,主库上执行一个大事务花了n分钟,那么大概率就会导致从库延迟n分钟。
比如,磁盘空间快满了,需要归档一些历史数据,需要一次性删除大量历史数据。这时候和就会出现主从延迟。
解决思路:
1.业务允许的话,控制每个事务的数据量,分成多次操作。
参考与致谢:
1.《MySQL实战45讲》(林晓斌老师)