学习总结录 学习总结录
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
  • Java基础

  • Java集合

  • MySQL

    • MySQL深入01-查询语句
    • MySQL深入02-更新语句
    • MySQL深入03-事务隔离
    • MySQL深入04-深入浅出索引
    • MySQL深入05-全局锁、表锁、行锁
    • MySQL深入06-事务隔离再探
    • MySQL深入07-普通索引和唯一索引
    • MySQL深入08-为什么会选错索引
    • MySQL深入09-字符串字段加索引
    • MySQL深入10-脏页刷新
    • MySQL深入11-数据库表空间回收
    • MySQL深入12-count()
    • MySQL深入13-order by
    • MySQL深入14-正确显示随机消息
    • MySQL深入15-索引失效案例分析
    • MySQL深入16-查询一行数据执行慢
    • MySQL深入17-幻读
    • MySQL深入18-改一行语句锁问题
    • MySQL深入19-暂时提高数据库性能方案
    • MySQL深入20-这么保证数据不丢
    • MySQL深入21-主备一致的保证
    • MySQL深入22-高可用性的保证
    • MySQL深入23-备库延迟好几个小时
    • MySQL深入24-主库出问题,从库这么办
      • MySQL深入24-主库出问题,从库这么办
      • 基于位点的主备切换
      • 基于GTID的主备切换
      • 参考
    • MySQL深入25-读写分离的过期读问题
    • MySQL深入26-判断数据库是否出问题
    • MySQL深入27-误删数据的处理方案
    • MySQL深入28-kill不掉的语句
    • MySQL深入29-查询对内存的影响
    • MySQL深入30-join深入
    • MySQL深入31-join语句优化
    • MySQL深入32-临时表深入
    • MySQL深入33-内部临时表何时使用
    • MySQL深入34-InnoDB和Memory
    • MySQL深入35-自增主键为什么不连续
    • MySQL深入36-insert语句的锁
    • MySQL深入37-如何快速复制一张表
    • MySQL深入38-grant和flush privileges
    • MySQL深入39-分区表
    • MySQL深入40-自增id用完如何处理
  • Redis

  • JVM

  • 多线程

  • 计算机网络

  • Spring

  • Kafka

  • Elasticsearch

  • Python

  • 面试专题

  • 知识库
  • MySQL
旭日
2023-03-31
目录

MySQL深入24-主库出问题,从库这么办

# MySQL深入24-主库出问题,从库这么办

一个基本的一主多从结构:

image-20220627104916339

A 和 A’互为主备, 从库 B、C、D 指向的是主库 A。一主多从的设置,一般用于读写分离,主库负责所有的写入和一部分读,其他的读请求则由从库分担。

备库和从库的概念是不同的,虽然二者都是只读的,但是从库对外提供服务,而备库只是为主库提供备份

主库发生故障,主备切换后的结果:

image-20220627105134946

一主多从结构在切换完成后,A’会成为新的主库,从库 B、C、D 也要改接到 A’。正是由于多了从库 B、C、D 重新指向的这个过程,所以主备切换的复杂性也相应增加了。

接下来,我们重点深入一主多从的主备切换过程。

# 基于位点的主备切换

前置知识

当我们把节点B设置成节点A’的从库的时候,需要执行一条 change master 命令

CHANGE MASTER TO 
MASTER_HOST=$host_name 
MASTER_PORT=$port 
MASTER_USER=$user_name 
MASTER_PASSWORD=$password 
MASTER_LOG_FILE=$master_log_name 
MASTER_LOG_POS=$master_log_pos  

对应参数详情信息如下:

  • MASTER_HOST:主库的IP

  • MASTER_PORT:主库的端口

  • MASTER_USER:用户名

  • MASTER_PASSWORD:密码

  • MASTER_LOG_FILE: 日志文件名

  • MASTER_LOG_POS: 日志偏移量

对于最后一个参数,假设节点 B 是 A 的从库,本地记录的也是 A 的位点。但是相同的日志,A 的位点和 A’的位点是不同的。因此,从库 B 要切换的时候,就需要先经过“找同步位点”这个逻辑。

考虑到切换过程中不能丢数据,为了数据的可靠性,我们找位点的时候,总是要找一个“稍微往前”的,然后再通过判断跳过那些再从库B上已经执行过的事务。

一种取同步位点的方法是这样的:

  1. 等待新主库A’把中转日志(relay log)全部同步完成;
  2. 在 A’上执行 show master status 命令,得到当前 A’上最新的 File 和 Position;
  3. 取原主库A故障的时刻T;
  4. 用 mysqlbinlog 工具解析 A’的 File,得到 T 时刻的位点。

步骤4所得到的数据也并不精确:

假设在T这个时刻,主库A已经执行完成了一个insert语句插入了一行数据R,并且已经将 binlog 传给了 A’和 B,然后在传完的瞬间主库 A 的主机就掉电了。

这时候系统的状态如下:

  1. 在从库 B 上,由于同步了 binlog, R 这一行已经存在;
  2. 在新主库 A’上, R 这一行也已经存在,日志是写在 123 这个位置之后的;
  3. 我们在从库 B 上执行 change master 命令,指向 A’的 File 文件的 123 位置,就会把插入 R 这一行数据的 binlog 又同步到从库 B 去执行。

由于从库B上已经有了这一条新语句,但是步骤4得到的位点是在插入语句之前,所以就会把插入 R 这一行数据的 binlog 又同步到从库 B 去执行,那么就会出现主键冲突的问题,然后停止同步。

通常情况下,我们在切换任务的时候,要先主动跳过这些错误,有两种常用的方法:

  • 主动跳过一个事务
  • 通过设置slave_skip_errors参数,直接设置跳过指定的错误
    • 1062 错误是插入数据时唯一键冲突;
    • 1032 错误是删除数据时找不到行。

# 基于GTID的主备切换

上述的跳过一个事务和忽略错误的方法,虽然最终都可以建立从库B和新主库A’的主备关系,但这两种操作都很复杂,而且容易出错。5.6版本引入GTID,彻底解决这个问题。

GTID 的全称是 Global Transaction Identifier,也就是全局事务 ID,是一个事务在提交的时候生成的,是这个事务的唯一标识。它由两部分组成,格式如下:

GTID=server_uuid:gno
  • server_uuid 是一个实例第一次启动时自动生成的,是一个全局唯一的值;
  • gno 是一个整数,初始值是 1,每次提交事务的时候分配给这个事务,并加 1。

在MySQL的官方文档里,GTID格式定义如下:

GTID=source_id:transaction_id

source_id:server_uuid

transaction_id:事务提交的时候才会分配的。

在GTID模式下,备库 B 要设置为新主库 A’的从库的语法如下:

CHANGE MASTER TO 
MASTER_HOST=$host_name 
MASTER_PORT=$port 
MASTER_USER=$user_name 
MASTER_PASSWORD=$password 
master_auto_position=1 

其中,master_auto_position=1 就表示这个主备关系使用的是 GTID 协议。我们把现在这个时刻,实例 A’的 GTID 集合记为 set_a,实例 B 的 GTID 集合记为 set_b。接下来,我们就看看现在的主备切换逻辑。

  1. 实例 B 指定主库 A’,基于主备协议建立连接。
  2. 实例 B 把 set_b 发给主库 A’。
  3. 实例 A’算出 set_a 与 set_b 的差集,也就是所有存在于 set_a,但是不存在于 set_b 的 GTID 的集合,判断 A’本地是否包含了这个差集需要的所有 binlog 事务。
    • 如果不包含,表示 A’已经把实例 B 需要的 binlog 给删掉了,直接返回错误;
    • 如果确认全部包含,A’从自己的 binlog 文件里面,找出第一个不在 set_b 的事务,发给 B;
  4. 之后就从这个事务开始,往后读文件,按顺序取 binlog 发给 B 去执行。

在基于 GTID 的主备关系里,系统认为只要建立主备关系,就必须保证主库发给备库的日志是完整的。因此,如果实例 B 需要的日志已经不存在,A’就拒绝把日志发给 B。

在引入GTID后,一主多从的切换场景下,由于不需要找位点了,所以从库B、C、D 只需要分别执行 change master 命令指向实例 A’即可。严谨地说,主备切换不是不需要找位点了,而是找位点这个工作,在实例 A’内部就已经自动完成了。

# 参考

MySQL 实战 45 讲-极客时间 (opens new window)

#MySQL
上次更新: 2024/06/29, 15:13:44
MySQL深入23-备库延迟好几个小时
MySQL深入25-读写分离的过期读问题

← MySQL深入23-备库延迟好几个小时 MySQL深入25-读写分离的过期读问题→

最近更新
01
基础概念
10-31
02
Pytorch
10-30
03
Numpy
10-30
更多文章>
Theme by Vdoing | Copyright © 2021-2024 旭日 | 蜀ICP备2021000788号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式