MySQL高可用架构探秘:主从复制剖析、切换策略、延迟优化与架构选型

MySQL高可用的基石

在分布式系统中,单机节点在发生故障时无法提供服务,这可能导致长期的服务不可用,从而影响其他节点的运作,导致的后果非常严重

为了满足服务的高可用,往往是通过节点冗余(新增相同功能的从节点),当发生故障时进行主从切换,让从节点成为新的主节点来继续提供服务

比如:MySQL的主从、Redis的主从、MQ broker的主从...思想大体类似的

作为高可用的基石------主从架构功不可没,本篇文章就来聊聊MySQL的主从的一些细节

binlog

binlog作为逻辑上恢复数据的日志,是主从数据同步、数据恢复的基础

binlog分为三种格式:statement、row、mixed

statement :记录写操作的SQL,语句轻量、传输快,使用该格式可能会导致数据不一致(因为从机与主机所处的环境不同,比如从机时间与主机不同时,使用now()函数)

row : 记录数据的修改,数据量大、传输慢,误操作时可以恢复数据(反向操作),主从同步时数据一致

mixed :结合statement、row的优点,自动混合选择格式

大多数情况下都是选择格式为row,因为数据一致并且可以恢复数据

主从复制

往期文章中说过当收到写操作需要修改数据时,为了满足数据的一致性,会写undo log(原子性)、redo log(持久性)、binlog等日志

当主节点接收到写操作更改数据时,也需要对从节点进行数据的修改以此来达到数据一致

在主从复制数据依靠的就是binlog,大致流程分为三个阶段:

  1. 主节点dump线程监听binlog变动通知从节点
  2. 从节点使用IO线程接收binlog并将其写入本地 relay log(中继日志)
  3. 从节点使用SQL线程根据relay log恢复数据

在单机中写完日志即可提交事务响应,而在主从中根据响应阶段的不同,主从复制的方式分为多种:

同步复制:所有从节点都响应(恢复完数据)主节点才响应,性能差、数据强一致

异步复制:主节点通知完从节点就立马响应,性能最好,存在延迟有数据一致性问题

半同步复制:只要有一个从节点响应主节点就响应,一主一从下与同步复制一致,网络超时退化为异步复制

增强半同步复制:在半同步复制的基础上,主节点收到响应后才提交事务,数据一致性会比半同步好,但性能稍差

延迟复制:从节点延迟一段时间恢复数据,这样即使发生误操作也可以进行回滚数据

主从切换

当主机发生故障时需要将从机切换为主机

不同策略

一般中间件的主从切换都只能在CAP理论中满足其二,即在分区容错(P)下只能满足可靠(C)或可用(A)

binlog上会记录主节点写操作的时间,从节点会维护一个 seconds_behind_master 来记录主从延迟的时间

在可靠策略下,需要等到旧的从节点完成所有的数据恢复(即seconds_behind_master为0)才成为主节点,提供写服务

在此期间只提供读服务、无法提供写服务,因此可靠策略会损失一定的可用性,取决于主从延迟的时间

在可用策略下会立即将从节点设置为新的主节点提供读写服务,某些场景下可能导致数据不一致

假设id自增,记录格式为(id,name),新增数据a,b,c

  1. 主节点已经新增(1,a),(2,b),(3,c)时宕机
  2. 从节点可能只重做数据(1,a),(2,b) 而(3,c)还在中继日志中
  3. 此时旧的从节点成为新的主节点又继续提供写服务,需要新增d,新增完d后才将中继日志的数据进行恢复

如果使用的binlog格式为statement或mixed,则会新增为(3,d)和(4,c)

如果格式为row,则会主键冲突报错,新增(3,d)后中继日志为(3,c)

在可用策略下可能导致数据不一致,使用row会提前暴露数据不一致的问题

基于GTID的主从切换

GTID 全局事务ID

格式为 server_uuid:gno

server_uuid 为节点标识

gno 为事务标识(事务提交时获得,全局自增)

在进行主从切换时,每个从节点同步数据的日志偏移量都不同,一般会找最新偏移量的从节点为新的主节点(这个偏移量是需要运维去定位的)

在GTID 全局事务ID出来后,binlog中每个事务有对应的GTID则可以通过GTID自动定位偏移量,不用手动定位

主从延迟

来源

默认情况下主从复制会使用异步复制,而在主从架构下一般会使用读写分离,主机服务写操作,从机服务读操作

由于使用异步复制,主从之间的数据一致性会存在一定的延迟,物理上主从会放在同一机房中,网络通信忽略不计,成本最大的就是从机SQL线程解析日志恢复数据的过程

如果恢复数据是一些大事务时会导致很长的延迟,比如在主机上执行批量操作耗时5s,在从机上执行时也会耗时那么久(资源大概一致)

可能写完操作就会进行读操作,如果此时从库还未重做数据就会导致写完查不到的数据不一致情况

先来看看哪些情况可能会导致主从延迟太长:

  1. 业务高峰期频繁读写(高TPS),从机不仅要同步数据,还要处理读操作
  2. 处理大事务,大事务导致延迟时间太久
  3. 从机硬件配置低,导致跟不上主机IO速度
  4. 主从机器可能参数不同(缓冲池、IO参数...)
  5. 从机本身就是延迟复制

...

当主从延迟过长时可以考虑使用方案缩短延迟:

  1. 调整redo log\bin log刷盘策略,增强IO
  2. canal监听(通知改为监听)
  3. 从机并行复制

从机并行复制借助于redo log、bin log两阶段提交时,redo log prepare阶段不会有锁冲突,可以并行执行

并行复制就是基于两阶段提交中的组提交,可以调整以下两个参数拉长组提交的时间,减慢主机写,加快从机重做数据

binlog_group_commit_sync_delay 延迟多少微秒后才调用 fsync

binlog_group_commit_sync_no_delay_count 累积多少次以后才调用 fsync

数据不一致解决方案

为了避免长时间的主从延迟,从机应该和主机有相同的参数、配置,并且要避免大事务

在业务高峰期还是可能存在主从延迟导致数据不一致,需要使用一些方案进行避免:

  1. 沟通业务:等待一段时间,比如用户修改完资料后进行审核状态

  2. 强一致性的读也走主库 :这样就不存在主从延迟,使用方便,大量强一致性读操作就会导致主机压力大

  3. 等待从机没延迟(三种判断方式):

    • 比较 seconds_behind_master 是否为0,为0说明没延迟
    • 比较主从上的位点 Master_Log_File 和 Read_Master_Log_Pos(主库的最新位点)Relay_Master_Log_File 和 Exec_Master_Log_Pos(备库执行的最新位点)判断是否相同,相同则没延迟
    • 比较从机上GTID集合 Retrieved_Gtid_Set 和 Executed_Gtid_Set (备库收到的所有日志的 GTID 集合 和 备库所有已经执行完成的 GTID 集合)是否相同,相同则没延迟

    这个方案粒度大(实际上只需要判断事务是否重做,这里是一直判断是否有延迟),如果高峰期一直有延迟就会一直等待判断,不使用

  4. 修改主从复制方式为同步复制:数据强一致性,性能差

  5. 修改主从复制方式为半同步复制:一主一从下与同步复制相同,一主多从下查询不确定,需要判断该事务是否已重做

方案5需要做到细粒度的判断事务是否在从机上已经重做,有两种方式且实现较为复杂

判断偏移量

select master_pos_wait(file, pos,[timeout]) 用于判断当前偏移量是否已经超过该位置

file 为 binlog 文件,pos 为 偏移量,timeout为等待的时间

使用半同步复制时,一个从节点已经响应,其他从节点应该也是快要响应的状态,因此可以等待一段时间 50ms,100ms...

如果超时则可以在业务中再去查主机,要注意如果都超时就相当于又全打在主机上

通过该SQL能够以主库日志中偏移量的方式判断是否已执行该事务(已执行返回0):

  1. 写操作完成时顺便获取binlog文件和偏移量的信息
  2. 携带这两个参数加上超时时间使用该SQL判断是否已执行
  3. 如果返回0(已执行)则查从机,否则查主机(注意限流)

判断GTID

判断GTID的思路与上面相似

select wait_for_executed_gtid_set(gtid_set, [timeout])

SQL的作用是判断是否已经执行GTID集合 返回0,超时返回1

流程类似:

  1. 写操作时获取GTID集合
  2. 根据GTID集合判断从机是否已执行事务
  3. 已执行查询,否则查主库或限流

主从架构

由于binlog的数据复制,主从架构可以非常丰富,想怎么搭就怎么搭

一主一从:主负责写,从负责读,读写压力平分

一主多从:主负责写,从负责读,适合读多于写

双主热备:两个节点互为主从,读写压力平分,但存在循环同步的问题

当AB节点互为主从时,A收到写请求,要把bin log给B重做,B重做完(相当于写请求)又会把bin log给A重做,这样就会导致循环同步数据

在同步数据时携带节点的id(server id)解决循环同步问题

A收到写请求,binlog给B并携带自己的id,B重做完又把binlog给A,A发现binlog上server id是自己则不进行重做

总结

本篇文章以MySQL高可用为起点,聊到MySQL中的主从复制、切换、延迟、架构等

binlog的statement格式记录SQL,数据量小、传输快,但可能导致数据不一致

binlog的row格式记录修改数据,数据量大,传输慢,可以修复误操作数据

binlog的mixed混用statement、row的优点,在可用策略的主从切换还是会导致数据不一致

主从复制时主机dump线程监听binlog变更通知从机拉取,从机io线程将日志写入realy log中继日志,再由sql线程解析日志重做数据

同步复制需要所有从机响应,拥有强一致性,但性能最差

默认的异步复制性能最好,但可能延迟时会有数据一致性

半同步复制只需要一个从机响应,多从下性能好于同步复制,网络超时会使用异步复制

增强的半同步复制会在从机响应时才提交事务,相比于半同步复制一致性略好

延迟复制可以让从机延迟一段时间重做数据,误操作数据可以恢复

主从切换时只能满足CAP中其二,满足可靠会导致一段时间不可写,满足可用可能会出现数据不一致

把从机参数、配置调整为主机相同,避免使用大事务可以避免主从延迟太长

当主从延迟太长可以通过调整从机IO参数增强IO能力

发生主从延迟的数据一致性问题时:

  1. 沟通业务,能否使用审核等中间状态,等延迟过了再查看
  2. 强制走主机,注意压力可能太大
  3. 使用同步复制,性能差
  4. 使用半同步复制,一主多从下需要判断事务是否执行(偏移量/GTID),实现困难

常用的主从架构有:一主一从、一主多从、双主热备(通过server id解决循环同步问题)...

最后(不要白嫖,一键三连求求拉~)

本篇文章被收入专栏 MySQL进阶之路,感兴趣的同学可以持续关注喔

本篇文章笔记以及案例被收入 gitee-StudyJavagithub-StudyJava 感兴趣的同学可以stat下持续关注喔~

有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~

关注菜菜,分享更多干货,公众号:菜菜的后端私房菜

相关推荐
ruleslol7 小时前
MySQL的段、区、页、行 详解
数据库·mysql
独自归家的兔7 小时前
Spring Cloud核心架构组件深度解析(原理+实战+面试高频)
spring cloud·面试·架构
天若有情6737 小时前
校园二手交易系统实战开发全记录(vue+SpringBoot+MySQL)
vue.js·spring boot·mysql
奋进的芋圆8 小时前
DataSyncManager 详解与 Spring Boot 迁移指南
java·spring boot·后端
それども8 小时前
MySQL affectedRows 计算逻辑
数据库·mysql
是小章啊8 小时前
MySQL 之SQL 执行规则及索引详解
数据库·sql·mysql
计算机程序设计小李同学8 小时前
个人数据管理系统
java·vue.js·spring boot·后端·web安全
Echo娴9 小时前
Spring的开发步骤
java·后端·spring
追逐时光者9 小时前
TIOBE 公布 C# 是 2025 年度编程语言
后端·.net