基本的一主多从结构:

图中,A和A'互为主备,从库BCD指向主库A。一主多次的设置,一般用于读写分离,主库负责所有的写入和一部分读,从库负责其他的读请求。

当主库发生故障,主备切换:

一主多从结构在切换完成后,A'会成为新主库,从库需要改接到A',而这个过程会增加主备切换的复杂度。接下来,就看看切换系统会怎么完成该切换过程。

基于位点的主备切换

当把节点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、MASTER_PORT、MASTER_USER、MASTER_PASSWORD代表主库A'的IP、端口、用户名和密码;

  • MASTER_LOG_FILE和MASTER_LOG_POS表示,要从主库的master_log_name文件的master_log_pos位置的日志继续同步。该位置就是所说的同步位点,也就是主库对应的文件名和日志偏移量。

之前节点B是A的从库,本地记录的是A的位点。但相同的日志,A的位点和A'的位点是不同的,因此从库B要切换时就需要先经过找同步位点的逻辑。

位点的一般获取方式是,考虑到切换过程不能丢数据,找的时候总是要找一个“稍微往前”的,然后再通过判断跳过在从库B上已经执行过的事务。

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

  • 等待新主库A'把中转日志relay log全部同步完成;

  • 在A'上执行show master status命令,得到当前A'上最新的File和position;

  • 取原主库A故障的时刻T;

  • 用mysqlbinlog工具解析A'的File,得到T时刻的位点。

mysqlbinlog File --stop-datetime=T --start-datetime=T

图中的123就表示A'实例在T时刻写入新的binlog的位置,那么就可以把123这个值作为$master_log_pos,用在节点B的change master命令里。

这个值并不精确,比如在T时刻,主库A已经执行完一个insert语句插入一行数据R,且已经将binlog传给了A'和B,在传完瞬间主库A的主机掉电。那么此时系统状态为:

  • 从库B已经同步了binlog,R这一行已经存在;

  • 新主库A'上,R也存在,日志写在123这个位置之后;

  • 在从库B上执行change master,指向A'的File文件的123位置,会把插入R的binlog又同步到从库B上执行。

此时从库B的同步线程就会报告Duplicate entry 'id_of_R' for key ’PRIMARY'错误,提示出现主键冲突,然后停止同步。

因此通常切换任务时要先主动跳过这些错误,有两种常用的方法:

一种是主动跳过一个事务,命令为:

set global sql_slave_skip_counter=1;
start slave;

因为切换过程中,可能会不止重复执行一个事务,所以需要在从库B刚开始接到新主库A’时,持续观察,每次碰到这些错误就停下来,执行一次跳过命令,直到不再出现停下来的情况,以此来跳过可能涉及的所有事务。

另一种是通过设置slave_skip_errors参数直接设置跳过指定的错误。

在执行主备切换有两类经常会遇到的错误:

  • 1062错误是插入数据时唯一键冲突;

  • 1032错误是删除数据时找不到行。

因此可以把slave_skip_errors设置为1032和1062。

需要注意的是,这种直接跳过指定错误的方法,针对的是主备切换时,由于找不到精确的同步位点,所以只能采用这种方法来创建从库和新主库的主备关系。

在主备切换过程中直接跳过这两类错误是无损的,因此可以这样设置。等到主备间的同步关系建立完成,并稳定执行一段时间之后,还需要把这个参数设置为空,以免之后真出现主从数据不一致也跳过。

GTID

前面的两种方法操作都较为复杂,且容易出错,因此MySQL 5.6引入了GTID,彻底解决了这个困难。

GTID全程是Global Transaction Identifier,即全局事务ID,是一个事务在提交时生成的,是这个事务的唯一标识,格式为:

GTID=server_uuid:gno

其中:

  • server_uuid是一个实例第一次启动时自动生成的,是一个全局唯一的值;

  • gno是一个整数,初始值为1,每次提交事务的时候分配给这个事务,并加一。

GTID模式的启动是,在启动一个MySQL实例的时候,加上参数gtid_mode=onenforce_gtid_consistency=on。在GTID模式下,每个事务都会跟一个GTID一一对应,GTID有两种生成方式,而使用哪种方式取决于session变量gtid_next的值:

  • gtid_next=automatic代表使用默认值,即分配server_uuid:gno;

  • gtid_next是一个指定的值,比如指定为current_gtid

    • 如果current_gtid已存在于实例的GTID集合中,接下来执行的这个事务会直接被系统忽略;

    • 如果current_gtid没有存在于实例的GTID集合中,就把这个current_gtid分配给接下来要执行的事务,也就是说系统不需要给这个事务生成新GTID,因此gno不用加1。

一个current_gtid只能给一个事务使用。这个事务提交后,如果要执行下一个事务,就要执行set命令,把gtid_next设置成另外一个gtid或者automatic。这样,每个MySQL实例都维护了一个GTID集合,用来对应“这个实例执行过的所有事务”。

接下来用一个例子帮助理解。在实例X中创建一个表t:

CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB; insert into t values(1,1);

初始化数据的binlog:

事务BEGIN前有@@SESSION.GTID_NEXT命令,此时如果实例X有从库,将binlog同步过去执行的话,执行事务前会先执行这两个set命令,这样图中的两个GTID都会被加入从库的GTID集合。

假设现在实例X是另外一个实例Y的从库,并且此时在实例Y上执行了插入语句:

insert into t values(1,1);

且这条语句在实例Y上的GTID是 “aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10”。那么实例X要同步过来执行时会出现主键冲突,导致实例X同步线程停止,此时处理方法是执行下面的语句序列:

set gtid_next='aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10';
begin;
commit;
set gtid_next=automatic;
start slave;

前三条语句的作用是通过提交一个空事务,把该GTID加到实例X的GTID集合中,这样再执行start slav 命令让同步线程执行起来的时候,虽然实例X上还是会继续执行实例Y传过来的事务,但是由于“aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10”已经存在于实例X的GTID集合,所以实例X就会直接跳过这个事务,也就不会再出现主键冲突的错误。

start slave前的set gtid_next=automatic作用是恢复GTID的默认分配行为,即如果之后有新的事务再执行,还是按照原有方式,继续分配gno=3

基于GTID的主备切换

在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,在实例B上执行start slave命令,取binlog的逻辑为:

  • 实例B指定主库A',基于主备协议建立连接;

  • 实例B把set_b发给主库A';

  • 实例A'算出set_a和set_b的差集,判断A'本地是否包含差集需要的所有binlog事务。

    • 如果不包含,表示A'已经把实例B需要的binlog给删除了,直接返回错误;

    • 如果确认全部包含,A'从自己的binlog里找出第一个不在set_b的事务发给B;

  • 之后从该事务开始,往后读文件,按顺序取binlog发给B执行。

在基于GTID的主备关系里,系统认为只要建立主备关系,就必须保证主库发给备库的日志是完整的。因此,如果实例B需要的日志已经不存在,A’就拒绝把日志发给B。而基于位点的协议,是由备库决定的,备库指定哪个位点,主库就发哪个位点,不做日志的完整性判断。

基于上面的介绍,再看引入GTID后,一主多从的切换场景下如何实现主备切换。由于不需要找位点,所以从库BCD只需要分别执行change master命令指向实例A'即可。

GTID和在线DDL

在之前的文章MySQL 22中,提到过业务高峰期的慢查询性能问题,分析如果是由于索引缺失引起的性能问题,可以通过在线加索引解决,但考虑到避免新增索引对主库性能造成的影响,可以先在备库加索引,然后再切换。在双M结构下,备库执行的DDL会传给主库,为了避免传回对主库造成影响,要通过set sql_log_bin=off关掉binlog。

此时会有疑问,这样操作数据库里加了索引,但binlog未记录,是否会导致数据和日志不一致?

假设这两个互为主备关系的库是实例X和实例Y,且当前主库是X,并且都打开了GTID模式。这时主备切换流程可以变成下面这样:

  • 在实例X上执行 stop slave;

  • 在实例Y上执行DDL语句。注意,这里并不需要关闭binlog;

  • 执行完成后,查出这个DDL语句对应的GTID,并记为server_uuid_of_Y:gno;

  • 到实例X上执行以下语句序列:

    set GTID_NEXT="server_uuid_of_Y:gno";
    begin;
    commit;
    set gtid_next=automatic;
    start slave;

    这样做的目的在于,既可以让实例Y的更新有binlog记录,同时也可以确保不会在实例X上执行这条更新。

  • 接下来,执行完主备切换,照着上面的流程再执行一遍即可。

MySQL 27 主库出问题了,从库怎么办?的更多相关文章

  1. mysql 主从同步出问题,重新修复从库 - web架构研究

    mysql 主从同步出问题,重新修复从库 - web架构研究     mysql 主从同步出问题,重新修复从库    0     昨天由于操作失误,在从库上执行一堆sql之后,导致主从同步错误,并且已 ...

  2. 004.MySQL主库手动复制至从库

    一 主库手动复制至从库 1.1 Master主库锁表 mysql> flush tables with read lock; Query OK, 0 rows affected (0.00 se ...

  3. 面试官:Mysql 中主库跑太快,从库追不上怎么整?

    写这篇文章是因为之前有一次删库操作,需要进行批量删除数据,当时没有控制好删除速度,导致产生了主从延迟,出现了一点小事故. 今天我们就来看看为什么会产生主从延迟以及主从延迟如何处理等相关问题. 坐好了, ...

  4. MySQL主库手动复制至从库

    原文转自:https://www.cnblogs.com/itzgr/p/10233932.html作者:木二 目录 一 主库手动复制至从库 1.1 Master主库锁表 1.2 主库备份 1.3 从 ...

  5. MySQL主从复制——主库已有数据的解决方案

    在上篇文章中我们介绍了基于Docker的Mysql主从搭建,一主多从的搭建过程就是重复了一主一从的从库配置过程,需要注意的是,要保证主从库my.cnf中server-id的唯一性.搭建完成后,可以在主 ...

  6. MySQL主从复制读写分离如何提高从库性能-实战

    在做主从读写分离时候,需要注意主从的一些不同参数设置,来提高从库的性能,提高应用读取数据的速度,这样做很有必要的. 做读写分离复制主从参数不同设置如下(需要根据自己应用实际情况来设置): parmet ...

  7. Oracle DataGuard主库丢失归档日志后备库的RMAN增量恢复一例

    第一部分  问题描述和环境状态确认 ----1. 问题场景 Oracle DataGuard主库丢失archivelog,如何不重建备库完成同步? 在Oracle DataGuard主从同步过程中可能 ...

  8. 安卓智能POS终端手持机PDA应用仓库出入库,移库,盘点,销售开单系统

    随着移动互联网的兴起,目前仓储管理所面临的的问题可以迎刃而解,WMS仓库系统解决方案通过智能终端扫描条码技术应用解决了工作量大导致工作效率不高,以及数据实时传输等问题,该方案主要提供仓库出入库,移库, ...

  9. 配置mysql数据库时出再错误:LookupError: No installed app with label 'admin'.

    版本: windows10+py37+django2.2 错误: 项目启动时出现,No installed app with label 'admin' 解决办法: 安装最新的 pip install ...

  10. mysql 主库有数据通过锁库做主从

    master@localhost[(none)]> grant replication slave on *.* to 'repl'@'192.168.1.177' identified by ...

随机推荐

  1. .NET SDK样式项目打包时如何将引用项目打进同一个包

    此篇为上一篇[<.NET SDK样式项目打包时如何将项目引用转为包依赖>](https://www.cnblogs.com/cnsharp/p/18819771 ")的姊妹篇. ...

  2. FastAPI权限缓存:你的性能瓶颈是否藏在这只“看不见的手”里?

    title: FastAPI权限缓存:你的性能瓶颈是否藏在这只"看不见的手"里? date: 2025/06/23 05:27:13 updated: 2025/06/23 05: ...

  3. PHP中设置时区方法

    在程序PHP 5以上版本的程序代码中使用函数ini_set('date.timezone','Asia/Shanghai');或者date_default_timezone_set('Asia/Sha ...

  4. MKL库解线性最小二乘问题(LAPACKE_dgels)

    LAPACK(Linear Algebra PACKage)库,是用Fortran语言编写的线性代数计算库,包含线性方程组求解(AX=B).矩阵分解.矩阵求逆.求矩阵特征值.奇异值等.该库用BLAS库 ...

  5. 卸载vivo或iqoo或其它手机的预装软件

    前言 众说周知,现在安卓手机做的越来越闭源,(除了一加和小米以及红蓝厂的部分型号 大部分)根本无法root. 那就意味着 手机上一些预装的软件 根本无法卸载 比如:阅读.xx官网.自带的视频和音乐软件 ...

  6. Luogu P11131 【MX-X5-T3】「GFOI Round 1」Cthugha 题解

    P11131 [MX-X5-T3]「GFOI Round 1」Cthugha 有意思的最短路题目,需要对迪杰斯特拉算法有深入的理解. 首先,不存在最小值的条件是相邻的两个格子加起来值小于 \(0\), ...

  7. CF162J Brackets 题解

    CF162J Brackets 看到两位用栈匹配括号的大佬,这里提供另一个思路. 对于括号的问题,我们考虑区间DP. 设状态 \(dp[i][j]\) 表示使区间 \([i,j]\) 内的括号匹配需要 ...

  8. Rust修仙之道 第十六章:文源境 · 字符符咒与灵文操控之法 (第一季完结篇)

    第十六章:文源境 · 字符符咒与灵文操控之法 "术可传者,以文为媒:术可验者,以符为印." 顾行云开启"文源宫",面对来自仙盟术士的最终考验: 「请解析百行灵符 ...

  9. 使用math库给我们定一个一些常用常量

    简介 使用math库给我们定一个一些常用常量 参考链接 https://www.quantstart.com/articles/Mathematical-Constants-in-C/ code #d ...

  10. 小心误关了NAS服务器!修改Linux的电源键功能

    前言 事情是这样的 今天想用NAS上的服务突然发现NAS离线了 我看了下原来是关机了 很奇怪,这几天也没断电啊- 我又去分析了系统日志 注意到了关机前的这段日志 Jul 13 23:24:33 pve ...