MYSQL系列高可用部署和异常恢复

2023年 7月 14日 75.7k 0

本文主要总结MYSQL高可用相关的介绍和总结
MYSQL采用主从架构来支持高可用。主从架构中必须有一个主节点,以及一个或多个从节点,所有的数据都会先写入到主,接着其他从节点会复制主节点上的增量数据,从而保证数据的最终一致性,使用主从复制方案,可以进一步提升数据库的可用性和性能:

  • 在主节点宕机或故障的情况下,从节点能自动切换成主节点的身份,从而继续对外提供服务。
  • 提供数据备份的功能,当主节点的数据发生损坏时,从节点中依旧保存着完整数据。
  • 可以基于主从实现读写分离,主节点负责处理写请求,从节点处理读请求,进一步提升性能。

但无论任何技术栈的主从架构,都会存在致命硬伤,同时也会存在些许问题需要解决:

  • 硬伤:木桶效应,一个主从集群中所有节点的容量,受限于存储容量最低的哪台服务器。
  • 数据一致性问题:由于同步复制数据的过程是基于网络传输完成的,所以存储延迟性。
  • 脑裂问题:从节点会通过心跳机制,发送网络包来判断主机是否存活,网络故障情况下会产生多主。

上述提到的三个问题中,第一个问题只能靠加大服务器的硬件配置解决,第二个问题相对来说已经有了很好的解决方案(后续讲解),第三个问题则是部署方式决定的,如果将所有节点都部署在同一网段,基本上不会出现集群脑裂问题。

主从复制

采用Binlog来进行主从复制

image.png

上述即是主从同步数据的原理图,但在讲解之前先来了解一下两种数据同步的方式:

  • 主节点推送:当主节点出现数据变更时,主动向自身注册的所有从节点推送新数据写入。
  • 从节点拉取:从节点定期去询问一次主节点是否有数据更新,有则拉取新数据写入。

MySQL究竟采用的是什么方式呢?其实是从拉的方案,但对其稍微做了一些优化,传统的从拉方案是需要从节点一直与主节点保持长连接,从节点定时或持续性的对主节点做轮询,查看主机的数据是否发生了变更,而MySQL的数据同步原理如下:

  • 客户端将写入数据的需求交给主节点,主节点先向自身写入数据。
  • 数据写入完成后,紧接着会再去记录一份Bin-log二进制日志。
  • 配置主从架构后,主节点上会创建一条专门监听Bin-log日志的log dump线程。
  • log dump线程监听到日志发生变更时,会通知从节点来拉取数据。
  • 从节点会有专门的I/O线程用于等待主节点的通知,当收到通知时会去请求一定范围的数据。
  • 当从节点在主节点上请求到一定数据后,接着会将得到的数据写入到relay-log中继日志。
  • 从节点上也会有专门负责监听relay-log变更的SQL线程,当日志出现变更时会开始工作。
  • 中继日志出现变更后,接着会从中读取日志记录,然后解析日志并将数据写入到自身磁盘中。
  • Bin-log日志格式:

    • Statment:记录每一条会对数据库产生变更操作的SQL语句(默认格式)。
    • Row:记录具体出现变更的数据(也会包含数据所在的分区以及所位于的数据页)。
    • MixedStatment、Row的结合版,可复制的记录SQL语句,不可复制的记录具体数据。

    现网一般使用Mixed格式

    主从模型不同架构

    一主多从架构

    image.png

    读多于写,读写分离,读操作从节点查询

    双主架构

    image.png
    典型的双主架构,这里有两个MySQL节点,它们都是主库,也都属于对方的从库,也就是两者之间会相互同步数据,这时为了防止主键出现冲突,一般都会通过设置数据库自增步长的方式来防重,通常会将两个节点的自增步长设为2,然后为两个节点分配自增初始值1、2

    多主一从架构

    image.png

    级联复制架构

    image.png

    这个过程中,第一层从库只有一个节点,它会负责从主库上拉取最新的数据,接着第二层的多个从库会从上一层的从库中拉取数据,这样能够这在从库较多的情况下,尽量降低数据同步对主节点的性能影响。

    主从架构小结

    • 一主多从架构:适用于读大于写的场景,采用多个从库来分担数据库系统的读压力。

    • 多主架构:适用于读写参半的场景,采用多个主库来承载数据库系统整体的读写压力。

    • 多主一从架构:适用于写大于读的场景,采用多个主库分担写压力,单个从库承载读压力。

    • 级联复制架构:适用于读大于写的场景,采用单个从节点来分担从库对主库造成的I/O压力。

    实际使用时复用上述多个架构,我们采用双主(一主一备)+ 多从 + 级联(给大数据、研发查询使用,实时性要求不是太高)

    主从复制数据方式

    MySQL中一共支持四种数据同步方式,即:异步复制、同步复制、半同步复制、增强式半同步复制/无损复制

    异步复制

    当主节点接收到一个客户端的写请求后,先会往自身写入数据,自身数据写入成功后,就会立马向客户端返回写入成功的信息,对于从节点的数据,会在其他的时间内再异步复制过去。

    异步复制参考 【MySQL】主从异步复制配置

    同步复制

    当主节点接收到一个客户端的写请求后,先会往自身写入数据,接着会去要求其他从节点也写入数据,当从节点的数据写入完成后,最终才会向客户端返回写入成功。

    MYSQL自身不支持同步复制

    半同步复制

    当客户端的数据到来时,会先写入到主节点中,接着主节点会向所有从节点发送一个写入数据的请求,但半同步模式中无需等待所有从节点全部写入完成后再返回,而是只要有一个从节点写入成功并返回了ACK,则会直接向客户端返回写入成功,这样既能够保证性能,又能够确保数据不丢失。

    同时为了避免网络延迟造成主库长时间收不到从库的ACK,因此在配置半同步式复制时,会有一个rpl_semi_sync_master_timeout参数来控制超时时间,其默认值是10000ms/10s,如若主库在10s内依旧未收到从库的ACK,则会将复制模式切换成异步模式,切成异步模式后,会在后续网络正常后再次切回半同步模式。

    参考 MySQL半同步复制原理与配置详解

    增强式半同步复制/无损复制

    增强式半同步复制也被称为无损复制,这是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-logPOS同步点,然后执行change master to [new-master-pos]命令,将其他从节点指向新主库,但每个从节点可能同步数据的进度都不一致,因此每个从节点都需要去找到它上次的POS点,然后指向新主库,这个工作是不是听起来就比较繁杂?答案是Yes,不过到了MySQL5.6版本后,开启了GTID复制后,则无需手动寻找POS点!

    GTID(Global Transaction ID)也就是全局事务标识符的意思,它由节点UUID+事务ID两部分组成,MySQL在第一次启动时都会利用UUID随机生成一个server_idMySQL会对每一个写事务都分配一个顺序递增的值作为事务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中,对于并行复制技术提出了真正的解决之道,也就是基于writesetMTS技术。

    啥叫基于writesetMTS技术呢?即多个事务之间,只要变更的数据记录没有重叠,也就是操作的数据没有冲突,无需在一个事务组内,也可以支持并发执行,这也是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。整个故障转移过程对应用程序完全透明

    image.png

    搭建方式参考 MySQL MHA 高可用集群部署及故障切换

    MySQL cluster

    分布式协议可以很好解决数据一致性问题。
    MySQL cluster是官方集群的部署方案,通过使用NDB存储引擎实时备份冗余数据,实现数据库的高可用性和数据一致性。

    image.png

    优点:

  • 全部使用官方组件,不依赖于第三方软件;

  • 可以实现数据的强一致性;

  • 缺点:

  • 国内使用的较少;

  • 配置较复杂,需要使用NDB储存引擎,与MySQL常规引擎存在一定差异;

  • 至少三节点;

  • 备份和异常恢复方式

    参考 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

    参考:
    1.(二十四)全解MySQL之主从篇:死磕主从复制中数据同步原理与优化
    2.五大常见的MySQL高可用方案

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论