对于一般的数据库来说,都是读多写少的应用场景

图片

虚线的箭头是主备关系,A和A’互为主备,从库BCD都是从库指向了A,主库负责所有的写和一部分读,其他的读由从库分担

那么A发生了故障,会出现的情况为

图片

这个架构导致了在A转换为了A’ BCD等从库也指向了BCD

整体需要的操作如下

1.当我们把节点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

分别需要主库的ip,端口 账号 密码

以及主库的文件名称和日志的位置,这个位置是同步的位点,也就是日志的偏移量

那么如何设置这两个参数呢

A和A’两个日志的位点是不同的,因此从库B必须找A’上的位点

因为切换的过程中不能丢失数据,于是找位点的时候,需要稍微靠前

一般来说,去取位点的方法为,等待主库A’把中转日志同步完成

然后执行 show master status命令,获取到最新File和Position

取原主库 A 故障的时刻 T

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

但是可能出现一定的偏差

因为一个主机A已经完成执行了insert语句,并且发给了所有的备库

但是A突然断电了,A’和B上都有这个日志,但是A’的日志中,这一记录是在主机掉电后出现的,也就是位置123之后的

在从库上执行这个change master命令,指向了A’ File文件的123位置,导致这一行会被传给从库B去执行

从库B发现了这一个错误,出现了主键冲突,导致了停止同步

对于这种错误

第一种做法:跳过这个事务

跳过这个事务的命令写法为

set global sql_slave_skip_counter=1;

start slave;

这样如果执行过程中有多个冲突事务,那么可能需要跳过多次

或者使用一个参数 slave_skip_errors设置为一个范围,这样在这个范围内出现的错误,就直接跳过

比如 1062是插入数据时候唯一键冲突

1032是删除数据时候找不到

slave_skip_errors设置了这两个就是碰到这两个错误时就直接跳过

后来,mysql提供了GTID跳过,全称为Global Transaction Identifier,全局事务ID,一个事务在提交的时候生成的

GTID的启动设置也很简单,在MySQL启动的时候,加上参数gtid_mode=on和enforce_gtid_consistency=on

内部结构为GTID=server_uuid:gno

这个GTID有两种生成方式

手动和自增

1.如果是设置了gtid_next=automatic,就是自增,记录binlong会先记录@@SESSION.GTID_NEXT=‘server_uuid:gno’;

2.如果是设置了一个指定的值,那么就会将这个current_gtid分配给要执行的事务,因此gno也不需要加1,但是要执行下一个命令,需要手动set,设置gtid_next为另外一个gtid

在binlog的显示为:

图片

可以看出,无论是插入数据还是建表,在之前都有一个@@SESSION.GTID_NEXT的命令

那么从库在同步binlog之后,就会先执行这两个SET命令

想要跳过一个事务,如何办呢?

假如一个事务的GTID是“aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10”

可以如下的执行

set gtid_next=“aaaaaaaa-cccc-dddd-eeee-ffffffffffff:10”

bengin;

commit;

set gtid_next=automatic;

start slave;

开启一个空事务,并直接提交,跳过这个GTID

然后set automatic来继续自动增加

那么,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协议,其中不用设置 MASTER_LOG_FILE和MASTER_LOG_POS参数了

这个整体逻辑改为了

B设置主库为A’

B的GTID集合为set_b

A’的GTID集合为set_a

1.那么建立连接后

2,B将set_b发给了主库A

3.实例A’算出了set_a和set_b的差集,也就是存在于set_a,但不存在于set_b的

如果全部包含,则一次性拿到B上缺失的事务,直接发给B

如果其中具有不包含的,则直接返回错误

对于想要解决因为没有加索引而导致慢查询的状况,可以通过在线加索引,而在线加索引可能导致主库新能影响,于是现在备库加索引,然后再切换到主库

但如果部署的为双M结构,那么备库执行了DDL语句也会传给主库,为了避免对主库造成影响,那么需要通过set sql_log_bin=off来关掉binlog

但是关了之后,可能导致数据和日志不一致啊

于是我们可以利用GTID来跳过这条语句

首先停止复制–stop slave,

然后执行DDL,

执行完成后,查出DDL语句对应的GTID,

在X上执行语句

set GTID_NEXT=”server_uuid_of_Y:gno”;

begin;

commit;

set gtid_next=automatic;

start slave;

从而可以保证不会执行DDL,也保证binlog记录

最后一个问题,GTID 模式下设置主从关系的时候,从库执行 start slave 命令后,主库发现需要的 binlog 已经被删除掉了,导致主备创建不成功,怎么办呢?

1.如果业务允许主从不一致的情况,那么可以在主库上先执行 show global variables like ‘gtid_purged’,得到主库已经删除的 GTID 集合,假设是 gtid_purged1;然后先在从库上执行 reset master,再执行 set global gtid_purged =‘gtid_purged1’;最后执行 start slave,就会从主库现存的 binlog 开始同步。binlog 缺失的那一部分,数据在从库上就可能会有丢失,造成主从不一致。

2.最好重新搭建主从

3.如果有其他的从库保留有全量的 binlog 的话,可以把新的从库先接到这个保留了全量 binlog 的从库,追上日志以后,如果有需要,再接回主库。

发表评论

邮箱地址不会被公开。 必填项已用*标注