基于GTID的Mysql主从复制

基于GTID的Mysql主从复制

前言

前一篇文章搭建了传统方式的主从复制,很简单,但是也存在着很多问题,比如主从复制的延迟,主从复制的数据不一致,主从关系切换复杂以及主从切换导致的数据不一致问题,而随着Mysql版本的更新,在Mysql5.6支持了基于了GTID(全局事务性ID)提供了传统复制没有的一些额外功能,生产环境下也都大范围应用GTID复制。下面我们一起来学习下。

什么是GTID

GTID  即全局事务id(globle transaction id identifier)  ,整体由两部分组成<master/slave>的 UUID 和事务TID ,这里面的UUID即server_id存放于data 目录下的auto.cnf文件中。TID 代表了该实例已经提交的事务量,并且随着事务提交单调递增,所以GTID能够保证每个MYSQL实例事务的执行(不会重复执行同一个事务,并且会补全没有执行的事务)GTID能够为主上提交的事务在整个复制集群中(master+slave)拥有唯一的ID。GTID最初由goole实现,官方在Mysql5.6加入该功能。但是mysql主从架构在一主一从情况下使用gtid没有什么优势,而对于双主以上的架构优势特别明显(因为故障切换,重新指定主从,如果按照传统方式操作麻烦,容易出错),可以在不丢失数据的情况下切换新主。

注意:基于GTID模式的主从复制,在主从关系成立前,master执行的操作也会在slave上执行。比如说在master上执行drop,delete清理操作在salve也会操作,有时可能会引起复制失败 。也就是说这个事务是从最先开始的事务日志开始的,即使是在主从复制搭建好之前。

GTID的工作原理

从服务器连接到主服务器后,把已经执行过的GTID(Executed_Gtid_Set: 即已经执行的事务编码 类属于SQL线程),获取到的GTID((Retrieved_Gtid_Set: 即从库已经接收到主库的事务编号 类属于IO线程) 都发给主服务器,主服务器把从服务器缺少的GTID以及对用的事务发送过去让从补全。(类似学生写作业,老师去检查这个原理)。当主服务器挂掉,根据gtid,找出同步最成功的那台服务器。直接把他提升为主即可。如果非要指定某一台不是最新的从服务器提升为主。先change让他和同步最全的从服务器进行数据补全。然后再提升为主

GTID是mysql5.6的新特性,可简化mysql的主从切换以及Failover。GTID用于再Binlog唯一标识的一个事务,当事务提交时,MYSQL server在写Binlog时候,会先写一个特殊的binlog_event,类型为gtid_event.指定下一个事务的GTID,然后再写事务的binlog,主从同步时GTID_Event 和事务的Binlog都会传到从库,从库在执行的时候也是用同样的GTID和binlog,这样主从同步后,就可以通过GTID确定从库同步到的位置了。也就是说无论级联复制还是一主多从情况,都可以通过GTID自动找点,而无需通过以前的file 和pos 找点

简单归纳:

1.master在更新数据,会在生成事务前产生GTID进行标记,然后一同记录到binlog日志中去。

2.slave端的I/O线程获取master的binlog日志,写入到本地的relay log中

3.SQL线程从relay log中获取GTID,然后对比本地的relay log是否有记录,如果有则这部分不会被复制,如果没有则执行该GTID标记的事务,并记录到binlog,在解析过程中会判断是否有主键,如果有就用二级索引,如果没有就全部扫描。

4.从服务器将收到的GTID((Retrieved_Gtid_Set)和已经执行的GTID(Executed_Gtid_Set),发送给主服务器,主服务器进行检查,把从服务器缺少的GTID以及对应的事务发送给他让其补全。就这样周而复始,从而保证主从同步数据一致。

GTID的优点

  • 故障转移后的主从角色切换更加方便,减少人工干预和故障时间(change不用再指定file 和pos位置)
  • 一个事务对应一个唯一的id,一个gtid在一个服务器上只会执行一次。保障了数据的一致性

下面用图来举例:

server2跟上了master的同步,server3没跟上,此时把server2提升为主,server3变为server2的从,此时在server3上执行change要进行一些计算。

而如果是基于gtid的模式就很简单,由于同一事务(都来源Master)的GTID在所有节点都一致,那么根据server3当前停止点的GTID就能定位到server2上的GTID,  change 中的 master_auto_position就代替了以前的file和pos

GTID的不足

1.不支持非事务引擎(非innodb),因为复制是基于事务的

2.不支持create tables xxx select 语句复制(主库直接报错),原因是会生成两个sql,一个是DDL创建表SQL,一个是insert into 插入数据的SQL;由于DDL会导致自动提交,所以这个sql至少需要两个GTID,但是GTID模式下,只能给这个SQL生成一个GTID.

3.在一个复制组对,必须统一开启GTID或者关闭

4.开启GTID需要重启(mysql5.7除外,支持在线生效)

5.对于create temporary table 和 drop temporary table语句不支持;

6.不支持sql_slave_skip_counter(传统复制方式通过设置该参数跳过错误事务,然后重新复制),但是基于GITD方式的复制,主库会把从库缺失的GTID发送给从,因此对于冲突的情形,需要注入空的事务来实现 ,(效果等同于sql_slave_skip_counter)

备注:sql_slave_skip_counter 在开启gtid失效是因为,从库跳过这个事务后,由于没执行,主库由于前面提到的检查机制,检测到这个错误事务没执行仍会发送给从库。所以又循环了。解决的办法就是将这个错误事务注入为空事务,然后提交。主库发现已经执行了,就跳过了。

步骤:

stop slave;

set gtid_next=’xxxxxxx:N’; 指定跳过的下一个事务的GTID

begin;

commit;注入一个空事务

set gtid_next=’AUTOMATIC’ 自动寻找GTID事务

start slave; 开始同步       

补充:上述步骤在从库进行,gtid_next填写要跳过的事务,然后提交一个空事务占据这个GTID.主库会发现下一个事务的GTID已经执行过了。就会跳过这个事务了。

开启GTID的一些条件与准备

1.mysql5.7开始支持在线开启GTID,之前的版本需要重启才可以

2.slave必须设置read_only=on,开启后不影响从库复制,复制防止业务在从库执行sql,导致主从数据不一致。但是对于具有super权限的root用户还是可以写的。所以如果像避免的话可以执行flush tables with read lock 。

3.保证当前slave的事务id为1(没有写)

部署

提示:mysql5.7的安装参考我之前写的文章,这里直接部署GTID主从复制

  • master授权复制的用户

grant replication slave,replication client on *.* to’repl’@192.168.137.102 identified by ‘123456’;

备注:之前为了方便都是grant all privileges ,生产环境下为了规范授权还是够用就行,不要放权存在安全隐患,尤其是数据库这么关键的服务

这里给了slave复制所需要的两个权限:relpication slave 和replication client 两个权限。

授权后可以在salve上机器通过授权账号登陆master验证授权是否成功。

master配置文件

server-id = 1 
gtid_mode = on
log-bin = msyql-bin
sync_binlog = 1
binlog_cache_size = 4M
max_binlog_cache_size = 2G
max_binlog_size = 1G
expire_logs_days = 7
gtid_mode = on
enforce_gtid_consistency = 1 #开启强制gtid一致性
log_slave_updates = 1  #也可以写成on ,5.7版本非必须,5.6必须写
binlog_format = row
binlog_checksum = 1
relay_log_recovery = 1
relay-log-purge = 1
master-info-repository=TABLE #定义master-info 记录在table中
relay-log-info-repository=TABLE #定义relay-log-info 记录在table中
sync-master-info=1 #启用之可确保无信息丢失;任何一个事务提交后, 将二进制日志的文件名及事件位置记录到文件中
slave-parallel-workers=2 #设定从服务器的SQL线程数;0表示关闭多线程复制功能
binlog-checksum=CRC32 #启用复制有关的校验功能
master-verify-checksum=1
slave-sql-verify-checksum=1
binlog-rows-query-log_events=1

slave配置文件

server-id = 2
relay-log=relay-log #定义relay-log名字,默认是以主机名,为了解除和主机名的耦合,采用自定义名字。防止更改主机名引起麻烦。
read-only = on
gtid_mode = on
log-bin = msyql-bin
sync_binlog = 1
binlog_cache_size = 4M
max_binlog_cache_size = 2G
max_binlog_size = 1G
expire_logs_days = 7
gtid_mode = on
enforce_gtid_consistency = 1 #开启强制gtid一致性
log_slave_updates = 1
binlog_format = row
binlog_checksum = 1
relay_log_recovery = 1
relay-log-purge = 1
master-info-repository=TABLE #定义master-info 记录在table中
relay-log-info-repository=TABLE #定义relay-log-info 记录在table中
sync-master-info=1 #启用之可确保无信息丢失;任何一个事务提交后, 将二进制日志的文件名及事件位置记录到文件中
slave-parallel-workers=2 #设定从服务器的SQL线程数;0表示关闭多线程复制功能
binlog-checksum=CRC32 #启用复制有关的校验功能
master-verify-checksum=1
slave-sql-verify-checksum=1
binlog-rows-query-log_events=1

  • 从库执行change信息更改:

首先stop slave 执行:

mysql> CHANGE MASTER TO
MASTER_HOST=’192.168.137.101′,
MASTER_PORT=3306,
MASTER_USER=’repl’,
MASTER_PASSWORD=’123456′,
MASTER_AUTO_POSITION=1;

start slave

参数验证

查看 master GTID情况

查看master status

在master上查看slave的hosts情况

查看slave status复制情况

*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.137.101
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: msyql-bin.000004
Read_Master_Log_Pos: 822
Relay_Log_File: relay-log.000004
Relay_Log_Pos: 414
Relay_Master_Log_File: msyql-bin.000004
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 822
Relay_Log_Space: 615
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: a5b19f7c-2b84-11e9-aa54-000c29afc2fb
Master_Info_File: mysql.slave_master_info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set: 6daf4f09-2b8f-11e9-9117-000c29baac9f:1,
a5b19f7c-2b84-11e9-aa54-000c29afc2fb:1-4
Auto_Position: 1

测试故障转移

摘自:散尽浮华博客

server1 挂掉,server2成为主,server3 执行change 填写server2信息即可,很简单这里不做演示。

跳过错误复制

同步时候出现这个报错

未使用GTID复制之前,只需要set global sql_slave_skip_counter=1;但是gtid不支持。需要注入空事务,前面提到过。

详细请看:https://www.cnblogs.com/kevingrace/p/5569753.html

里面有很多错误实例。

如何从传统复制切换为GTID复制?

1.首先确保你是5.7版本,只有5.7版本以上才支持在线升级GTID模式

  • 每台server执行

mysql> SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = WARN;

检查错误日志,直到没有错误出现,才可以进行下一步。

  • mysql> SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = ON;
  • mysql> SET @@GLOBAL.GTID_MODE = OFF_PERMISSIVE;
  • mysql> SET @@GLOBAL.GTID_MODE = ON_PERMISSIVE;
  • mysql > SHOW  STATUS  LIKE  ‘ONGOING_ANONYMOUS_TRANSACATION_COUNT’;

在每台server上执行 如果这个值等于0就可以,不需要一直为0.只要出现一次0就ok。

  • 确保所有anonymous 事务传递到slave上了

master执行:show master status;

slave执行:select master_pos_wait(file,position);

或者等一段时间,只要不是大的延迟都没问题。

  • 每台server上执行 :mysql> SET @@GLOBAL.GTID_MODE = ON;
  • 在每台server 上将my.cnf配置好gtid相关的参数

 

 
点赞

发表评论

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

Loading...