- 虚线箭头为 主从关系 ,
A和A'互为主从,B、C、D指向主库A - 一主多从的设置,一般用于 读写分离 ,主库负责 所有的写入 和 一部分读 ,其它读请求由从库分担
主库故障切换
A' 成为新的主库, B 、 C 、 D 指向主库 A'
基于位点的切换
B 原先是 A 的从库,本地记录的也是 A 的位点,但 相同的日志 , A 的位点与 A' 的位点是 不同 的
-- 节点B设置为节点A'的从库 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
寻找位点
- 很难精确,只能大概获取一个位置
- 由于在切换过程中 不能丢数据 ,在寻找位点的时候,总是找一个 稍微往前的位点 ,跳过那些已经在
B执行过的事务
常规步骤
- 等待新主库
A'将所有relaylog全部执行完 - 在
A'上执行SHOW MASTER STATUS,得到A'上最新的File和Position - 获取原主库
A发生故障的时刻T - 使用
mysqlbinlog解析A'的File,得到时刻T的位点
-- end_log_pos=123,表示在时刻T,A'写入新binlog的位置,作为B的CHANGE MASTER TO命令的MASTER_LOG_POS参数 $ mysqlbinlog /var/lib/mysql/slave-bin.000009 --start-datetime='2019-02-26 17:44:00' --stop-datetime='2019-02-26 17:45:00' | grep end_log_pos #190226 17:42:01 server id 2 end_log_pos 123 CRC32 0x5b852e9b Start: binlog v 4, server v 5.7.25-log created 190226 17:42:01 at startup
位点不精确
- 假设在时刻
T,原主库A已经执行完成了一个INSERT语句,插入一行记录R- 并且已经将
binlog传给A'和B,然后原主库A掉电
- 并且已经将
- 在
B上,由于已经同步了binlog,R这一行是已经存在的 - 在新主库
A'上,R这一行也是存在的,日志写在了123这个位置之后 - 在
B上执行CHANGE MASTER TO,执行A'的File文件的123位置- 就会把插入
R这一行数据的binlog又同步到B去执行 -
B的同步线程会报 重复主键 错误,然后停止同步
- 就会把插入
跳过错误
方式1:主动跳过一个事务,需要 持续观察 ,每次碰到这些错误,就执行一次跳过命令
SET GLOBAL sql_slave_skip_counter=1; START SLAVE;
方式1:设置 slave_skip_errors=1032,1062 , 1032 错误是删除数据时 找不到行 , 1062 错误是插入数据时报 唯一键冲突
在 主从切换过程 中,直接跳过 1032 和 1062 是 无损 的,等主从间的同步关系建立完成后,需要将 slave_skip_errors 恢复为 OFF
mysql> SHOW VARIABLES LIKE '%slave_skip_errors%'; +-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | slave_skip_errors | OFF | +-------------------+-------+
基于GTID的切换
- GTID: Global Transaction Identifier, 全局事务ID
- 在事务 提交 时生成,是事务的唯一标识,组成
GTID = server_uuid:gno-
server_uuid是实例第一次 启动 时自动生成的,是一个 全局唯一 的值 -
gno是一个整数,初始值为1,每次 提交事务 时分配,+1
-
- 官方定义:
GTID = source_id:transaction_id-
source_id即server_uuid -
transaction_id容易造成误解-
transaction_id一般指事务ID,是在事务 执行过程 中分配的,即使事务 回滚 了,事务ID也会 递增 - 而
gno只有在事务 提交 时才会分配,因此GTID往往是 连续 的
-
-
- 开启
GTID模式,添加启动参数gtid_mode=ON和enforce_gtid_consistency=ON - 在
GTID模式下,每个事务都会跟一个GTID一一对应,生成GTID的方式由参数gtid_next(Session)控制 - 每个 MySQL 实例都维护了一个
GTID集合,用于表示: 实例执行过的所有事务
gtid_next
-
gtid_next=AUTOMATIC,MySQL会将server_uuid:gno分配给该事务- 记录
binlog时,会先记录一行SET @@SESSION.GTID_NEXT=server_uuid:gno,将该GTID加入到本实例的GTID集合
- 记录
-
gtid_next=UUID:NUMBER,通过SET @@SESSION.GTID_NEXT=current_gtid执行- 如果
current_gtid已经 存在 于实例的GTID集合中,那么接下来执行的这个事务会直接被系统 忽略 - 如果
current_gtid并 没有存在 于实例的GTID集合中,那么接下来执行的这个事务会被分配为current_gtid -
current_gtid只能给 一个事务 使用,如果执行下一个事务,需要把gtid_next设置成另一个GTID或者AUTOMATIC
- 如果
-- gtid_next=AUTOMATIC mysql> SHOW BINLOG EVENTS IN 'master-bin.000003'; +-------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | Log_name | Pos | Event_type | Server_id | End_log_pos | Info | +-------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------+ | master-bin.000003 | 4 | Format_desc | 1 | 123 | Server ver: 5.7.25-log, Binlog ver: 4 | | master-bin.000003 | 123 | Previous_gtids | 1 | 194 | b8502fe3-3b4a-11e9-9562-0242ac110002:1-5 | | master-bin.000003 | 194 | Gtid | 1 | 259 | SET @@SESSION.GTID_NEXT= 'b8502fe3-3b4a-11e9-9562-0242ac110002:6' | | master-bin.000003 | 259 | Query | 1 | 484 | GRANT REPLICATION SLAVE ON *.* TO 'replication'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9' | | master-bin.000003 | 484 | Gtid | 1 | 549 | SET @@SESSION.GTID_NEXT= 'b8502fe3-3b4a-11e9-9562-0242ac110002:7' | | master-bin.000003 | 549 | Query | 1 | 643 | CREATE DATABASE test | +-------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------+
表初始化
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
mysql> SHOW MASTER STATUS; +-------------------+----------+--------------+------------------+-------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +-------------------+----------+--------------+------------------+-------------------------------------------+ | master-bin.000004 | 877 | | | b8502fe3-3b4a-11e9-9562-0242ac110002:1-12 | +-------------------+----------+--------------+------------------+-------------------------------------------+ mysql> SHOW BINLOG EVENTS IN 'master-bin.000004'; +-------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+ | Log_name | Pos | Event_type | Server_id | End_log_pos | Info | +-------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+ | master-bin.000004 | 4 | Format_desc | 1 | 123 | Server ver: 5.7.25-log, Binlog ver: 4 | | master-bin.000004 | 123 | Previous_gtids | 1 | 194 | b8502fe3-3b4a-11e9-9562-0242ac110002:1-9 | | master-bin.000004 | 194 | Gtid | 1 | 259 | SET @@SESSION.GTID_NEXT= 'b8502fe3-3b4a-11e9-9562-0242ac110002:10' | | master-bin.000004 | 259 | Query | 1 | 373 | use `test`; DROP TABLE `t` /* generated by server */ | | master-bin.000004 | 373 | Gtid | 1 | 438 | SET @@SESSION.GTID_NEXT= 'b8502fe3-3b4a-11e9-9562-0242ac110002:11' | | master-bin.000004 | 438 | Query | 1 | 620 | use `test`; CREATE TABLE `t` ( `id` INT(11) NOT NULL, `c` INT(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB | | master-bin.000004 | 620 | Gtid | 1 | 685 | SET @@SESSION.GTID_NEXT= 'b8502fe3-3b4a-11e9-9562-0242ac110002:12' | | master-bin.000004 | 685 | Query | 1 | 757 | BEGIN | | master-bin.000004 | 757 | Table_map | 1 | 802 | table_id: 109 (test.t) | | master-bin.000004 | 802 | Write_rows | 1 | 846 | table_id: 109 flags: STMT_END_F | | master-bin.000004 | 846 | Xid | 1 | 877 | COMMIT /* xid=27 */ | +-------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
- 事务
BEGIN之前有一条SET @@SESSION.GTID_NEXT - 如果实例X有从库Z,那么将
CREATE TABLE和INSERT语句的binlog同步到从库Z执行- 执行事务之前,会先执行两个
SET命令,这样两个GTID就会被加入到从库Z的GTID集合
- 执行事务之前,会先执行两个
主键冲突
- 如果实例X是实例Y的从库,之前实例Y上执行
INSERT INTO t VALUES (1,1)- 对应的
GTID为aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:12 - 实例X需要同步该事务过来执行,会报 主键冲突 的错误,实例X的同步线程停止,处理方法如下
- 对应的
-- 实例X提交一个空事务,将该GTID加到实例X的GTID集合中 SET gtid_next='aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:12'; BEGIN; COMMIT; -- 实例X的Executed_Gtid_Set已经包含了aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:12 mysql> SHOW MASTER STATUS; +-------------------+----------+--------------+------------------+------------------------------------------------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +-------------------+----------+--------------+------------------+------------------------------------------------------------------------------------+ | master-bin.000004 | 1087 | | | aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:12,b8502fe3-3b4a-11e9-9562-0242ac110002:1-12 | +-------------------+----------+--------------+------------------+------------------------------------------------------------------------------------+ -- 恢复GTID的默认分配行为 SET gtid_next=AUTOMATIC; -- 实例X还是会继续执行实例Y传过来的事务 -- 但由于实例X的GTID集合已经包含了aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:12,因此实例X会直接跳过该事务 START SLAVE;
主从切换
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协议,不再需要指定MASTER_LOG_FILE和MASTER_LOG_POS - 实例
A'的GTID集合记为set_a,实例B的GTID集合记为set_b - 实例
B执行START SLAVE,取binlog的逻辑如下
START SLAVE
- 实例
B指定新主库A',基于 主从协议 建立连接 - 实例
B把set_b发送给A' - 实例
A'计算出seb_a和set_b的GTID差集(存在于set_a,但不存在于set_b的GTID集合)- 判断实例
A'本地是否包含了 差集需要的所有binlog事务 - 如果 没有全部包含 ,说明实例
A'已经把实例B所需要的binlog删除掉了,直接返回错误 - 如果 全部包含 ,实例
A'从自己的binlog文件里面,找到第1个不在set_b的事务,发送给实例B- 然后从该事务开始,往后读文件,按顺序读取
binlog,发给实例B去执行
- 然后从该事务开始,往后读文件,按顺序读取
- 判断实例
位点 VS GTID
- 基于
GTID的主从关系里面,系统认为只要 建立了主从关系 ,就必须保证 主库发给从库的日志是完整 的 - 如果实例
B需要的日志已经不存在了,那么实例A'就拒绝将日志发送给实例B - 基于 位点 的协议,是由 从库决定 的,从库指定哪个位点,主库就发送什么位点,不做 日志完整性 的判断
- 基于
GTID的协议,主从切换 不再需要找位点 ,而找位点的工作在实例A'内部 自动完成
日志格式
- 切换前
- 实例
B的GTID集合:server_uuid_of_A:1-N
- 实例
- 新主库
A'自己生成的binlog对应的GTID集合:server_uuid_of_A':1-M - 切换后
- 实例
B的GTID集合:server_uuid_of_A:1-N,server_uuid_of_A':1-M
- 实例
参考资料
《MySQL实战45讲》
转载请注明出处:http://zhongmingmao.me/2019/02/27/mysql-master-slave-switch/
访问原文「MySQL -- 主从切换」获取最佳阅读体验并参与讨论
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Redis集群的主从切换研究
- RocketMQ 平滑升级到主从切换(实战篇)
- linux 双Redis + keepalived 主从复制+宕机自主切换
- 源码阅读技巧篇:RocketMQ DLedger 多副本即主从切换专栏回顾
- RocketMQ 整合 DLedger(多副本)即主从切换实现平滑升级的设计技巧
- 在 Docker 上搭建 pg_pool 实现 PG 主从自动切换
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
互联网+秋叶课堂:我的网课创业
秋叶 / 北京:机械工业出版社 / 2016-3-1 / 39.0
在线教育被很多人视为 “互联网+”创业热潮中的下一个风口,越来越多的老师和创业者选择在线教育创业。本书作者秋叶老师2013年双11正式带一帮小伙伴开始在线教育创业,在短短两年内“从0到1”,累计做到了超500万元的销售业绩,成为国内Office领域在线教育运营最成功的团队之一。在这本书中秋叶老师结合自己的实战经历,向各位完整剖析了两年创业的真实复盘思考,是一本值得在线教育创业同行入手的必读书。 ......一起来看看 《互联网+秋叶课堂:我的网课创业》 这本书的介绍吧!