前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >主库出问题了,从库怎么办?

主库出问题了,从库怎么办?

原创
作者头像
用户8639654
修改2021-08-11 18:08:39
4220
修改2021-08-11 18:08:39
举报
文章被收录于专栏:云计算运维

下图是一个基本的一主多从结构

图中,虚线箭头表示的是主备关系,也就是A和A’互为主备,从库B、C、D指向的是主库A。一主多从的设置,一般用于读写分离,主库负责所有的写入和一部分读,其他的读请求则由从库分担

一主多从结构在切换完成后,A’会成为新的主库,从库B、C、D也要改接到A’

1、基于位点的主备切换

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

代码语言:javascript
复制
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_HOSTMASTER_PORTMASTER_USERMASTER_PASSWORD四个参数,分别代表了主库A’的IP、端口、用户名和密码
  • 最后两个参数MASTER_LOG_FILEMASTER_LOG_POS表示,要从主库的master_log_name文件的master_log_pos这个位置的日志继续同步。而这个位置就是所说的同步位点,也就是主库对应的文件名和日志偏移量

找同步位点很难精确取到,只能取一个大概位置。一种去同步位点的方法是这样的:

  1. 等待新主库A’把中转日志全部同步完成
  2. 在A’上执行show master status命令,得到当前A’上最新的File和Position
  3. 取原主库A故障的时刻T
  4. 用mysqlbinlog工具解析A’的File,得到T时刻的位点,这个值就可以作为$master_log_pos

这个值并不精确,有这么一种情况,假设在T这个时刻,主库A已经执行完成了一个insert语句插入了一行数据R,并且已经将binlog传给了A’和B,然后在传完的瞬间主库A的主机就掉电了。那么,这时候系统的状态是这样的:

  1. 在从库B上,由于同步了binlog,R这一行已经存在
  2. 在新主库A’上,R这一行也已经存在,日志是写在master_log_pos这个位置之后的
  3. 在从库B上执行change master命令,指向A’的File文件的master_log_pos位置,就会把插入R这一行数据的binlog又同步到从库B去执行,造成主键冲突,然后停止tongue

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

一种是,主动跳过一个事务

代码语言:javascript
复制
set global sql_slave_skip_counter=1;
start slave;

另一种方式是,通过设置slave_skip_errors参数,直接设置跳过指定的错误。这个背景是,我们很清楚在主备切换过程中,直接跳过这些错误是无损的,所以才可以设置slave_skip_errors参数。等到主备间的同步关系建立完成,并稳定执行一段时间之后,还需要把这个参数设置为空,以免之后真的出现了主从数据不一致,也跳过了

2、GTID

MySQL5.6引入了GTID,是一个全局事务ID,是一个事务提交的时候生成的,是这个事务的唯一标识。它的格式是:

代码语言:javascript
复制
GTID=source_id:transaction_id
  • source_id是一个实例第一次启动时自动生成的,是一个全局唯一的值
  • transaction_id是一个整数,初始值是1,每次提交事务的时候分配给这个事务,并加1

GTID模式的启动只需要在启动一个MySQL实例的时候,加上参数gtid_mode=on和enforce_gtid_consistency=on就可以了

在GTID模式下,每个事务都会跟一个GTID一一对应。这个GTID有两种生成方式,而使用哪种方式取决于session变量gtid_next的值

  1. 如果gtid_next=automatic,代表使用默认值。这时,MySQL就把GTID分配给这个事务。记录binlog的时候,先记录一行SET@@SESSION.GTID_NEXT=‘GTID’。把这个GTID加入本实例的GTID集合
  2. 如果gtid_next是一个指定的GTID的值,比如通过set gtid_next=‘current_gtid’,那么就有两种可能:
  • 如果current_gtid已经存在于实例的GTID集合中,接下里执行的这个事务会直接被系统忽略
  • 如果current_gtid没有存在于实例的GTID集合中,就将这个current_gtid分配给接下来要执行的事务,也就是说系统不需要给这个事务生成新的GTID,因此transaction_id也不需要加1

一个current_gtid只能给一个事务使用。这个事务提交后,如果要执行下一个事务,就要执行set命令,把gtid_next设置成另外一个gtid或者automatic

这样每个MySQL实例都维护了一个GTID集合,用来对应这个实例执行过的所有事务。

3、基于GTID的主备切换

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

代码语言:javascript
复制
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的逻辑是这样的:

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

之后从这个事务开始,往后读文件,按顺序取binlog发给B去执行

4、GTID和在线DDL

如果是由于索引缺失引起的性能问题,可以在线加索引来解决。但是,考虑到要避免新增索引对主库性能造成的影响,可以先在备库加索引,然后再切换,在双M结构下,备库执行的DDL语句也会传给主库,为了避免传回后对主库造成影响,要通过set sql_log_bin=off关掉binlog,但是操作可能会导致数据和日志不一致

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

  • 在实例X上执行stop slave
  • 在实例Y上执行DDL语句。这里不需要关闭binlog
  • 执行完成后,查出这个DDL语句对应的GTID,记为source_id_of_Y:transaction_id
  • 到实例X上执行一下语句序列:
代码语言:javascript
复制
set GTID_NEXT="source_id_of_Y:transaction_id";
begin;
commit;
set gtid_next=automatic;
start slave;

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、基于位点的主备切换
  • 2、GTID
  • 3、基于GTID的主备切换
  • 4、GTID和在线DDL
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档