本文主要为大家带来MYSQL高可用相关的介绍和总结
MYSQL采用主从架构来支持高可用。主从架构中必须有一个主节点
,以及一个或多个从节点,所有的数据都会先写入到主,接着其他从节点会复制主节点上的增量数据,从而保证数据的最终一致性
,使用主从复制方案,可以进一步提升数据库的可用性和性能:
- 在主节点宕机或故障的情况下,从节点能自动切换成主节点的身份,从而继续对外提供服务。
- 提供数据备份的功能,当主节点的数据发生损坏时,从节点中依旧保存着完整数据。
- 可以基于主从实现读写分离,主节点负责处理写请求,从节点处理读请求,进一步提升性能。
但无论任何技术栈的主从架构,都会存在致命硬伤,同时也会存在些许问题需要解决:
- 硬伤:木桶效应,一个主从集群中所有节点的容量,受限于存储容量最低的哪台服务器。
- 数据一致性问题:由于同步复制数据的过程是基于网络传输完成的,所以存储延迟性。
- 脑裂问题:从节点会通过心跳机制,发送网络包来判断主机是否存活,网络故障情况下会产生多主。
上述提到的三个问题中,第一个问题只能靠加大服务器的硬件配置解决
,第二个问题相对来说已经有了很好的解决方案(后续讲解),第三个问题则是部署方式决定的,如果将所有节点都部署在同一网段,基本上不会出现集群脑裂问题。
主从复制
采用Binlog来进行主从复制
上述即是主从同步数据的原理图,但在讲解之前先来了解一下两种数据同步的方式:
- 主节点推送:当主节点出现数据变更时,主动向自身注册的所有从节点推送新数据写入。
- 从节点拉取:从节点定期去询问一次主节点是否有数据更新,有则拉取新数据写入。
那MySQL
究竟采用的是什么方式呢?其实是从拉的方案,但对其稍微做了一些优化,传统的从拉方案是需要从节点一直与主节点保持长连接,从节点定时或持续性的对主节点做轮询,查看主机的数据是否发生了变更,而MySQL
的数据同步原理如下:
- 客户端将写入数据的需求交给主节点,主节点先向自身写入数据。
- 数据写入完成后,紧接着会再去记录一份
Bin-log
二进制日志。 - 配置主从架构后,主节点上会创建一条专门监听
Bin-log
日志的log dump
线程。 - 当
log dump
线程监听到日志发生变更时,会通知从节点来拉取数据。 - 从节点会有专门的
I/O
线程用于等待主节点的通知,当收到通知时会去请求一定范围的数据。 - 当从节点在主节点上请求到一定数据后,接着会将得到的数据写入到
relay-log
中继日志。 - 从节点上也会有专门负责监听
relay-log
变更的SQL
线程,当日志出现变更时会开始工作。 - 中继日志出现变更后,接着会从中读取日志记录,然后解析日志并将数据写入到自身磁盘中。
Bin-log
日志格式:
Statment
:记录每一条会对数据库产生变更操作的SQL
语句(默认格式)。Row
:记录具体出现变更的数据(也会包含数据所在的分区以及所位于的数据页)。Mixed
:Statment、Row
的结合版,可复制的记录SQL
语句,不可复制的记录具体数据。
现网一般使用Mixed格式
主从模型不同架构
一主多从架构
读多于写,读写分离,读操作从节点查询
双主架构
典型的双主架构,这里有两个MySQL
节点,它们都是主库,也都属于对方的从库,也就是两者之间会相互同步数据,这时为了防止主键出现冲突,一般都会通过设置数据库自增步长的方式来防重,通常会将两个节点的自增步长设为2
,然后为两个节点分配自增初始值1、2
多主一从架构
级联复制架构
这个过程中,第一层从库只有一个节点,它会负责从主库上拉取最新的数据,接着第二层的多个从库会从上一层的从库中拉取数据,这样能够这在从库较多的情况下,尽量降低数据同步对主节点的性能影响。
主从架构小结
-
一主多从架构:适用于读大于写的场景,采用多个从库来分担数据库系统的读压力。
-
多主架构:适用于读写参半的场景,采用多个主库来承载数据库系统整体的读写压力。
-
多主一从架构:适用于写大于读的场景,采用多个主库分担写压力,单个从库承载读压力。
-
级联复制架构:适用于读大于写的场景,采用单个从节点来分担从库对主库造成的
I/O
压力。
实际使用时复用上述多个架构,我们采用双主(一主一备)+ 多从 + 级联(给大数据、研发查询使用,实时性要求不是太高)
主从复制数据方式
MySQL
中一共支持四种数据同步方式,即:异步复制、同步复制、半同步复制、增强式半同步复制/无损复制
异步复制
当主节点接收到一个客户端的写请求后,先会往自身写入数据,自身数据写入成功后,就会立马向客户端返回写入成功的信息,对于从节点的数据,会在其他的时间内再异步复制过去。
异步复制参考 【MySQL】主从异步复制配置
同步复制
当主节点接收到一个客户端的写请求后,先会往自身写入数据,接着会去要求其他从节点也写入数据,当从节点的数据写入完成后,最终才会向客户端返回写入成功。
MYSQL自身不支持同步复制
半同步复制
当客户端的数据到来时,会先写入到主节点中,接着主节点会向所有从节点发送一个写入数据的请求,但半同步模式中无需等待所有从节点全部写入完成后再返回,而是只要有一个从节点写入成功并返回了ACK
,则会直接向客户端返回写入成功,这样既能够保证性能,又能够确保数据不丢失。
同时为了避免网络延迟造成主库长时间收不到从库的
ACK
,因此在配置半同步式复制时,会有一个rpl_semi_sync_master_timeout
参数来控制超时时间,其默认值是10000ms/10s
,如若主库在10s
内依旧未收到从库的ACK
,则会将复制模式切换成异步模式,切成异步模式后,会在后续网络正常后再次切回半同步模式。
增强式半同步复制/无损复制
增强式半同步复制也被称为无损复制,这是MySQL5.7
版本中引入的一种新技术,在MySQL5.7
版本中就不存在普通的半同步模式了,当将复制模式配置成半同步时,默认就会选用无损复制模式,和之前传统的半同步复制区别在于:从after-commit
变成了after-sync
,啥意思呢?
after-commit
:主库在未收到从库的ACK
之前,虽然不会给客户端返回写入成功,但本质上在MySQL
中会提交事务,也就是主库中的其他事务是可以看见对应数据的,当此时出现宕机时,就会导致旧主上能查询出的数据,在新主(原本的从库)上无法查询出来了。after-sync
:当主库未收到从库的ACK
之前,也不会在主库上提交事务,也就是保证了主从节点的数据强一致性,解决了after-commit
中存在的问题。
其实简单来说,无损复制中等待ACK
的动作会放到事务提交前进行,而传统半同步复制中,等待ACK
的动作会放到事务提交后进行。两者之间的区别就在于:对主从节点的数据严格性不同,一般情况下,无损复制会比传统半同步复制开销更大一些,因为事务迟迟不提交,会导致对应的锁资源不会主动释放,其他需要获取对应锁资源的事务只能阻塞等待,这会造成主库的整体性能出现一定影响。
上面聊完了主从复制的四种数据同步方式,接着来聊一聊MySQL5.6
中,两种新的复制特性,即从库数据的延迟复制,和从库数据的并行复制,展开说说吧~
延迟复制
延迟复制通常用于一些特殊场景,它可以支持从库数据的延迟同步,也就是当从库上的I/O
线程,将主库的Bin-log
日志请求回来后,从节点的SQL
线程并不会立刻解析日志执行,而是等待一段时间后再解析日志执行,这个等待的时间可以由开发者来配置,一般建议设为3~6
小时之间。
防止误删操作
并行复制
并行复制是在MySQL5.6
加入的新技术,但实际上5.6
版本中的支持并不完善,直到5.7
版本中才真正实现了并行复制技术,但想要说清楚并行复制,则得先弄懂组复制的概念,想要弄明白组复制,在此之前还得理解GTID
复制的概念,所以并行复制技术会比前面几个概念难啃一些。
GTID复制
GTID
复制依旧是5.6
版本中的新功能,在传统的主从架构中,当需要发生主从切换时,需要开发/运维人员手动找到Bin-log
的POS
同步点,然后执行change master to [new-master-pos]
命令,将其他从节点指向新主库,但每个从节点可能同步数据的进度都不一致,因此每个从节点都需要去找到它上次的POS
点,然后指向新主库,这个工作是不是听起来就比较繁杂?答案是Yes
,不过到了MySQL5.6
版本后,开启了GTID
复制后,则无需手动寻找POS
点!
GTID(Global Transaction ID)
也就是全局事务标识符的意思,它由节点UUID
+事务ID
两部分组成,MySQL
在第一次启动时都会利用UUID
随机生成一个server_id
,MySQL
会对每一个写事务都分配一个顺序递增的值作为事务ID
,而GTID
则是由这两玩意儿组成的,格式为server_uuid:trx_id
。
当主库的事务有了这个全局事务标识后,再发生主从切换时就无需手动寻点了,仅需要执行change master to master_auto_position = 1
这条命令即可,它会自动去新主库上寻找数据的同步点,也就是MySQL
自身就具备断点复制的功能。
组复制
前面阐述的GTID
复制则是组复制的实现基础,而组复制则是并行复制的基础,那么什么叫做组复制呢?组复制是指将一组并行执行的事务,全部放入到一个GTID
中记录,后续从节点同步数据时,会一次性读取这一组事务解析并执行,与传统的GTID
区别如下:
- 传统的
GTID
值由节点ID
+事务ID
组成:12EEA4RD6-45AC-667B-33DD-CCC55EF718D:88
。 - 组复制的
GTID
通过逗号分隔:12EEA4RD6-45AC-667B-33DD-CCC55EF718D:89, 12EEA4RD6-45AC-667B-33DD-CCC55EF718D:89-94, ......
。
MySQL如何实现事务分组的呢?
看过MySQL
源码的小伙伴应该清楚,MySQL
提交事务时内部会调用ordered_commit
函数来处理相关工作,其函数执行的逻辑流程图如下:
当一个事务提交时都会调用ordered_commit
函数,首先会将事务加入等待事务组,接着会经过三个核心步骤:FLUSH、SYNC、COMMIT
,对应的也会有三个队列,它们三者的工作原理都大致相同:
-
①如果某个事务进入
FLUSH
队列时,该队列还是空的,则这个事务会担任“队长”的角色。 -
②当后续其他事务进入队列时,发现队列不为空,则会将提交工作委托给队长来完成。
-
③如上图中的「事务1」则是队长,后续的都是队员,但队长不会无限制等待队员到来:
- 从队长加入的时间点开始,当超出
binlog_group_commit_sync_delay
规定的时间后,就会进行一次组提交。
- 从队长加入的时间点开始,当超出
同一时刻只允许一组事务做这些工作,也就是当有另外一组事务提交时,需要等待上一组事务提交完成。
在做组提交工作时,会将当前事务组的内容记录到Bin-log
日志中,同时会将这组事务记录成一个GTID
,不同事务之间通过,
逗号分隔(实际过程更为复杂,这里只做简单讲解)。
经过上述步骤后,就实现了按事务组去生成
GTID
值,这也是后续并行复制的基础,接着往下看。
并行复制
到了MySQL5.7
中,才基于组复制技术实现了真正意义上的并行复制,因为能够在同一时间内提交的事务,绝对是不存在锁冲突的,所以可以开启多条线程同时执行一个组中不同的事务,但这个思想是从MariaDB
中照抄过来的~
一句话来总结就是:主库上是咋样并发写入数据的,从库也会开启对应的线程数去并发写入。
在5.7
中官方为这种机制命名为enhanced multi-threaded slave
,简称MTS
机制,同时为了兼容5.6
版本中的并行复制,又多加入了一个slave-parallel-type
参数:
DATABASE
:默认的并行复制模式,表示基于库级别的来完成并行复制。LOGICAL_CLOCK
:表示基于组提交的方式来完成并行复制。
但当你想要使用这种并行复制的技术,必须要将版本升级到MySQL 5.7.19
才行,因为在此之前的版本中,MTS
技术依旧存在不小瑕疵。
并行复制出现的意义是什么?
对于这个问题相信大家都能直接想出来答案,能够在很大程度上提升从库复制数据的速度,也就是能够让从库的数据实时性提升,尤其是无损复制模式中,主节点需要等待从节点的ACK
才会真正提交事务,从库使用并行复制后,能够在一定程度上解决从库的复制延迟问题。
不过虽然
5.7
中的并行复制,在一定程度上解决了原有的从库延迟问题,但如果一个新的从节点加入集群时,因为要从头开始同步数据,这种并行复制的模式依旧存在效率问题,而到了MySQL8.0
中,对于并行复制技术提出了真正的解决之道,也就是基于writeset
的MTS
技术。
啥叫基于writeset
的MTS
技术呢?即多个事务之间,只要变更的数据记录没有重叠,也就是操作的数据没有冲突,无需在一个事务组内,也可以支持并发执行,这也是MySQL-MTS
技术的最终的完美形态。
数据一致性解决方案
改写业务逻辑
读库差不多采用其他业务逻辑处理
更改复制方式
采用半同步方式,会导致性能下降严重
调整数据库架构
升级主库硬件配置,实时性要求高的查询从主库查询
引入第三方中间件
使用类似Canal,监听主节点的binlog,主动推送给从节点,提高主从同步效率
高可用部署方案
MHA方案
什么是 MHA
- MHA(MasterHigh Availability)是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。
- MHA 的出现就是解决MySQL 单点的问题。
- MySQL故障切换过程中,MHA能做到0-30秒内自动完成故障切换操作。
- MHA能在故障切换的过程中最大程度上保证数据的一致性,以达到真正意义上的高可用。
MHA组成
• MHA Node(数据节点)
MHA Node 运行在每台 MySQL 服务器上
• MHA Manager(管理节点)
MHA Manager 可以单独部署在一台独立的机器上,管理多个 master-slave 集群;也可以部署在一台 slave 节点上
MHA Manager 会定时探测集群中的 master 节点。当 master 出现故障时,它可以自动将最新数据的 slave 提升为新的 master, 然后将所有其他的 slave 重新指向新的 master。整个故障转移过程对应用程序完全透明
搭建方式参考 MySQL MHA 高可用集群部署及故障切换
MySQL cluster
分布式协议可以很好解决数据一致性问题。
MySQL cluster是官方集群的部署方案,通过使用NDB存储引擎实时备份冗余数据,实现数据库的高可用性和数据一致性。
优点:
-
全部使用官方组件,不依赖于第三方软件;
-
可以实现数据的强一致性;
缺点:
-
国内使用的较少;
-
配置较复杂,需要使用NDB储存引擎,与MySQL常规引擎存在一定差异;
-
至少三节点;
备份和异常恢复方式
延伸
问题1:如果MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?
可以考虑以下三种方法:
- 采用组提交方式,设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 参数,减少 binlog 的写盘次数。这个方法是基于“额外的故意等待”来实现的,因此可能会增加语句的响应时间,但没有丢失数据的风险。
- 将 sync_binlog 设置为大于 1 的值(比较常见是 100~1000)。这样做的风险是,主机掉电时会丢 binlog 日志。
- 将 innodb_flush_log_at_trx_commit 设置为 2(事务提交时会将事务日志写入磁盘,但不会刷新到磁盘,而是每秒将日志缓存写入操作系统的缓存中)。这样做的风险是,主机掉电的时候会丢数据。
问题2:什么时候会把线上生产库设置成“非双 1”
- 业务高峰期。一般如果有预知的高峰期,DBA 会有预案,把主库设置成“非双 1”。
- 备库延迟,为了让备库尽快赶上主库。@永恒记忆和 @Second Sight 提到了这个场景。
- 用备份恢复主库的副本,应用 binlog 的过程,这个跟上一种场景类似。
- 批量导入数据的时候。
一般情况下,把生产库改成“非双 1”配置,是设置 innodb_flush_logs_at_trx_commit=2、sync_binlog=1000
。