一篇读懂MySQL的GTID

GTID的出现,使每一个事务在集群中有了唯一性的意义

每提交一个事务,当前执行线程都会拿到一个唯一标示符GTID,此标示符不仅在源MySQL是唯一的,在复制环境中所有的MySQL示例中都是唯一的

通过show master status或者show slave status来查看

mysql> show master status \G
*************************** 1. row ***************************
             File: mysqlbinlog.000018
         Position: 194
     Binlog_Do_DB: 
 Binlog_Ignore_DB: 
Executed_Gtid_Set: 79e1e785-7d7c-11e9-b4e4-000c29209533:1-8

GTID的生命周期

1、在master上执行一个事务,master会产生一个GTID信息,并保存在binlog中

2、将binlog信息发送到slave,并保存在relay log 中

3、slave首先验证其是否已经在自己的binlog中使用过了该GTID,如果没有使用,slave则写入该GTID,并用应用其事务,并将事务写入自己的binlog

4、slave不会为该事务生成新的GTID,而是从gtid_next中读取GTID值并写入binlog中,用来标识事务,且在集群中该事务会始终对应这个GTID

gtid_executed表及其压缩

MySQL 5.7.5之后可以通过mysql库的gtid_executed表来查看GTID

gtid_mode为ON或者ON_PERMISSIVE时,GTID才会保存在mysql.gtid_executed中

mysql> show create table mysql.gtid_executed \G
*************************** 1. row ***************************
       Table: gtid_executed
Create Table: CREATE TABLE `gtid_executed` (
  `source_uuid` char(36) NOT NULL COMMENT 'uuid of the source where the transaction was originally executed.',
  `interval_start` bigint(20) NOT NULL COMMENT 'First number of interval.',
  `interval_end` bigint(20) NOT NULL COMMENT 'Last number of interval.',
  PRIMARY KEY (`source_uuid`,`interval_start`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> select * from mysql.gtid_executed;
+--------------------------------------+----------------+--------------+
| source_uuid                          | interval_start | interval_end |
+--------------------------------------+----------------+--------------+
| 79e1e785-7d7c-11e9-b4e4-000c29209533 |              1 |            7 |
| 79e1e785-7d7c-11e9-b4e4-000c29209533 |              8 |            8 |
+--------------------------------------+----------------+--------------+
2 rows in set (0.00 sec)

随着数据库的不断更新,mysql.gtid_executed会存入很多数据占用空间,所以服务器会定期对mysql.gtid_executed表进行压缩,压缩率通过gtid_executed_compression_period

mysql> show variables like '%executed_%';
+----------------------------------+-------+
| Variable_name                    | Value |
+----------------------------------+-------+
| gtid_executed_compression_period | 1000  |
+----------------------------------+-------+
'1000表示表的压缩在每1000个事务后执行,0代表不压缩'

还有一种压缩情况

当开启binlog,但是没有启用gtid_executed_compression_period时,binlog文件的切换,会引起mysql.gtid_executed的自动压缩

mysql.gtid_executed的压缩线程

mysql> select * from performance_schema.threads where name like '%gtid%' \G
*************************** 1. row ***************************
          THREAD_ID: 28
               NAME: thread/sql/compress_gtid_table
               TYPE: FOREGROUND
     PROCESSLIST_ID: 3
   PROCESSLIST_USER: NULL
   PROCESSLIST_HOST: NULL
     PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Daemon
   PROCESSLIST_TIME: 24278
  PROCESSLIST_STATE: Suspending
   PROCESSLIST_INFO: NULL
   PARENT_THREAD_ID: 1
               ROLE: NULL
       INSTRUMENTED: YES
            HISTORY: YES
    CONNECTION_TYPE: NULL
       THREAD_OS_ID: 1850

利用GTID搭建主从关系

GTID最方便的就是搭建和维护主从复制,替代了基于binlog position的主从复制

搭建GTID主从复制,要注意开启参数,MySQL 5.7.6之后可以在线搭建

gtid-mode=on enforce-gtid-consistency=1 log_bin=masterbinlog binlog_format=row 从库要开启skip-slave-start=1,避免slave启动之后,继续使用传统复制模式

搭建GTID主从的三种情况

1、master是新搭建的,没有数据

2、master搭建不久,所有binlog保存完整

1、2都可直接使用change master搭建

gtid-mode=on
enforce-gtid-consistency=1
log_bin=masterbinlog
binlog_format=row
从库要开启skip-slave-start=1,避免slave启动之后,继续使用传统复制模式

3、master有大量的数据,最原始的binlog已被删除,无法从头开始获取所有GTID

1、利用备份方式获取master的数据及GTID范围,使用innobackupex备份,会将该信息保存在xtrabackup_binlog_info中

[root@redhat-1-6 backup]# cat xtrabackup_binlog_info 
mysqlbinlog.000017	194	79e1e785-7d7c-11e9-b4e4-000c29209533:1-7

2、启动slave实例,并设置gtid_purged的值,跳过这段范围

SET @@GLOBAL.GTID_PURGED='79e1e785-7d7c-11e9-b4e4-000c29209533:1-7'

3、利用change master搭建主从复制

4、启动slave复制,会自动跳过这段GTID范围,拉去最新的GTID

如何跳过一个GTID

在复制中偶尔会遇到主键冲突或者从库找不到记录的错误,传统复制模式可以使用

mysql> set global sql_slave_skip_counter=1;

跳过这个事件,但是在GTID模式中,使用该命令则会报错

ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction

那我们如何跳过这个GTID呢

基于GTID模式的复制,跳过一个事务,需要利用一个空事务来完成

1、通过分析last_error的报错和master的binlog得到产生错误的GTID
2、stop slave
3、SET gtid_next='GTID';    '设置下一个GTID为我们要跳过的GTID'
4、begin;commit;            '执行一个空事务'
5、SET gtid_next='AUTOMATIC'   '设置GTID自动获取'
6、start slave

跳过事务使slave正常运行,在处理问题是非常方便的,但是可能会出现事务不一致的情况,所以在跳过之前,分析一下binlog并且记录下来,分析是否可以跳过,跳过之后查看主从是否一致,是否需要修复数据,所以一定要慎用!!

在线进行传统复制模式(基于binlog position)与GTID复制模式的转换

在线将传统复制模式改为GTID模式

1、在每一台服务器上设置enforce_gtid_consistency=warn

set @@global.enforce_gtid_consistency=warn;

这样设置之后,使得所有的事务都允许违反GTID的一致性,然后查看错误日志,在进行下一步之前,要确保错误日志中没有警告

2、在每一台服务器上设置enforce_gtid_consistency=on

set @@global.enforce_gtid_consistency=on;

确保所有的事务都不能违反GTID的一致性

3、在每台服务器上设置gtid_mode=off_permissive

set @@global.gtid_mode=off_permissive;

表示,新的事务是匿名的,同时允许复制的事务是为GTID或是匿名的

4、在每台服务器上设置gtid_mode=on_permissive

set @@global.gtid_mode=on_permissive;

表示新的事务使用GTID,同时允许复制的事务是为GTID或是匿名的

3,4都要确保执行才可以

5、确保Ongoing_anonymous_transaction_count=0

mysql> show status like '%Ongoing_anonymous_transaction_count%';
+-------------------------------------+-------+
| Variable_name                       | Value |
+-------------------------------------+-------+
| Ongoing_anonymous_transaction_count | 0     |
+-------------------------------------+-------+

该状态表示已标记为匿名的正在进行的事务数量,为0则表示无事务等待被处理

6、在每台服务器上设置gtid_mode=on,开启GTID

set @@global.gtid_mode=on;

7、修改my.cnf,修改之后,即使数据库重启,配置也是生效的

gtid-mode=on
enforce-gtid-consistency=1

注意:开启GTID之后复制还是基于binlog position的,还需将复制调整为GTID模式

stop slave;
change master to master_auto_position=1;
start slave;

在线将GTID模式改为传统复制模式

1、关闭基于GTID的复制模式,调整为传统复制模式

stop slave;  假设复制关闭后复制的位置为mysqlbinlog.000020:506
mysql> change master to master_auto_position=0,master_log_file='mysqlbinlob.000020',master_log_pos=506;
start slave;

2、在每台服务器上设置gtid_mode=on_permissive

3、在每台服务器上设置gtid_mode=off_permissive

4、等待所有服务器上的gtid_owned为空

mysql> show variables like '%gtid_owned%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| gtid_owned    |       |
+---------------+-------+

这表示正在由线程执行的全局GTID集合

5、等待所有的slave上都复制完成匿名事务

mysql> show status like '%Ongoing_anonymous_transaction_count%';
+-------------------------------------+-------+
| Variable_name                       | Value |
+-------------------------------------+-------+
| Ongoing_anonymous_transaction_count | 0     |
+-------------------------------------+-------+

6、在每一台服务器关闭GTID

set @@global.gtid_mode=off;

7、修改配置文件,确保重启配置也是生效的

gtid-mode=off
enforce-gtid-consistency=off

GTID的限制

由于基于GTID的复制依赖于事务,所以在使用GTID的时候,有些MySQL的特性是不支持的

1、事务中混合多个存储引擎,会产生多个GTID

当一个事务中的更新包含了非事务引擎(如MyISAM)和事务引擎(如innodb)表的操作,就会导致多个GTID分配给同一个事务

2、主从库的表存储引擎不一致,会导致数据的不一致

如果主从库的存储引擎不一致,例如一个是事务引擎,一个是非事务引擎,则会导致事务和GTID之间的一对一关系被破坏,从而导致基于GTID的复制不能正确进行

3、不支持create table ...select语句

因为基于行模式复制时,该语句实际上被记录为两个单独的事务,一个是创建表,另一个是将原表中的数据插入到新表中,一些情况下,这两个事务可能收到相同的事务id,这意味着包含插入的事务可能被从库跳过

4、不支持create_temporary table和drop temporary table

使用GTID复制时,不支持create_temporary table和drop temporary table,但是在autocommit=1的情况下可以创建临时表,master不产生GTID,所以不会同步到salve,但是在删除临时表的时候,产生GTID会导致主从中断

5、不推荐在GTID模式的实例上使用mysql_upgrade

因为mysql_upgrade的过程要创建或修改系统表(非事务引擎),所以不建议在开启GTID的实例上使用带有--write-binlog选项的mysql_upgrade

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值