您的当前位置:首页正文

MySQL5.6 新特性之GTID

2023-11-09 来源:帮我找美食网

      MySQL5.6在5.5的基础上增加了一些改进,本文章先对其中一个一个比较大的改进"GTID"进行说明。

概念:

      GTID即全局事务ID(global transaction identifier),GTID实际上是由UUID+TID组成的。其中UUID是一个MySQL实例的唯一标识。TID代表了该实例上已经提交的事务数量,并且随着事务提交单调递增。下面是一个GTID的具体形式:

4e659069-3cd8-11e5-9a49-001c4270714e:1-77

更具体的说明见官方说明。

GTID意义:

      引入GTID的意义是什么?

      1)因为清楚了GTID的格式,所以通过UUID可以知道这个事务在哪个实例上提交的。

      2)通过GUID可以极方便的进行复制结构上的故障转移,新主设置。很好的解决了下面这个图(图来自高性能MySQL第10章)的问题。

技术分享

上面图的意思是:Server1(Master)崩溃,根据从上show slave status获得Master_log_File/Read_Master_Log_Pos的值,Server2(Slave)已经跟上了主,Server3(Slave)没有跟上主。这时要是把Server2提升为主,Server3变成Server2的从。这时在Server3上执行change的时候需要做一些计算,这里就不做说明了,具体的说明见高性能MySQL第10章,相对来说是比较麻烦的。

这个问题在5.6的GTID出现后,就显得非常的简单。由于同一事务的GTID在所有节点上的值一致,那么根据Server3当前停止点的GTID就能定位到Server2上的GTID。甚至由于MASTER_AUTO_POSITION功能的出现,我们都不需要知道GTID的具体值,直接使用CHANGE MASTER TO MASTER_HOST=‘xxx‘, MASTER_AUTO_POSITION命令就可以直接完成failover的工作。

测试:

1)复制环境的搭建:具体的复制搭建的步骤可以在网上搜索

因为支持GTID,所以5.6多了几个参数:

mysql> show variables like ‘%gtid%‘;+---------------------------------+-----------+| Variable_name | Value |+---------------------------------+-----------+| binlog_gtid_simple_recovery | OFF || enforce_gtid_consistency | OFF || gtid_deployment_step | OFF || gtid_executed | || gtid_mode | OFF || gtid_next | AUTOMATIC || gtid_owned | || gtid_purged | || simplified_binlog_gtid_recovery | OFF |+---------------------------------+-----------+

主从环境的搭建和5.5没有什么区别,唯一需要注意的是:开启GTID需要启用这三个参数:

#GTIDgtid_mode = onenforce_gtid_consistency = 1log_slave_updates = 1

任意一个参数不开启则都会报错:

2015-08-09 02:33:57 6512 [ERROR] --gtid-mode=ON or UPGRADE_STEP_1 or UPGRADE_STEP_2 requires --log-bin and --log-slave-updates2015-08-09 02:33:57 6512 [ERROR] Aborting2015-08-09 02:39:58 9860 [ERROR] --gtid-mode=ON or UPGRADE_STEP_1 requires --enforce-gtid-consistency2015-08-09 02:39:58 9860 [ERROR] Aborting

具体的方法可以参考官方文档。

三个实例开启之后(3306、3307、3308),执行change的时候也要注意:

各个实例的uuid:

3306:mysql> select @@server_uuid;+--------------------------------------+| @@server_uuid |+--------------------------------------+| 4e659069-3cd8-11e5-9a49-001c4270714e |+--------------------------------------+3307:mysql> select @@server_uuid;+--------------------------------------+| @@server_uuid |+--------------------------------------+| 041d0e65-3cde-11e5-9a6e-001c4270714e |+--------------------------------------+3308:mysql> select @@server_uuid;+--------------------------------------+| @@server_uuid |+--------------------------------------+| 081ccacf-3ce4-11e5-9a95-001c4270714e |+--------------------------------------+

使用5.6之前的主从change:

mysql> change master to master_host=‘127.0.0.1‘,master_user=‘rep‘,master_password=‘rep‘,master_log_file=‘mysql-bin3306.000001‘,master_log_pos=151,/*master_auto_position=1*/;

报错:

ERROR 1776 (HY000): Parameters MASTER_LOG_FILE, MASTER_LOG_POS, RELAY_LOG_FILE and RELAY_LOG_POS cannot be set when MASTER_AUTO_POSITION is active.

当使用 MASTER_AUTO_POSITION 参数的时候,MASTER_LOG_FILE,MASTER_LOG_POS参数不能使用。

使用5.6之后的主从change:

mysql> change master to master_host=‘127.0.0.1‘,master_user=‘rep‘,master_password=‘rep‘,master_port=3306,master_auto_position=1;

在执行上面的命令的时候会报错2个warnings,主要的原因是复制账号安全的问题,相关的信息可以看这里。

从总体上看来,由于要支持GTID,所以不需要手工确定主服务器的MASTER_LOG_FILE及MASTER_LOG_POS。要是不需要GTID则需要指定FILE和POS。在2个从上执行上面命令,到此主从环境搭建完成。GTID的主从完成之后可以通过show processlist查看:

mysql> show processlistG;*************************** 1. row *************************** Id: 38 User: rep Host: localhost:52321 db: NULL Command: Binlog Dump GTID #通过GTID复制 Time: 48 State: Master has sent all binlog to slave; waiting for binlog to be updated Info: NULL Rows_sent: 0Rows_examined: 0

2)测试复制的故障转移

server1(3306)挂了,服务器起不来了。需要把其中的一个从设置为主,另一个设置为其的从库:

server2(3307):

 Master_Log_File: mysql-bin3306.000002 Read_Master_Log_Pos: 4156773 Exec_Master_Log_Pos: 4156773

server3(3308):

 Master_Log_File: mysql-bin3306.000001 Read_Master_Log_Pos: 83795320 Exec_Master_Log_Pos: 83795320

相比之下server2完成的事务要比server3更接近或则等于server1,现在需要把server3设置为server2的从库。

在MySQL5.6之前,这里的计算会很麻烦,要计算之前主库的log_pos和当前要设置成主库的log_pos,很有可能出错。所以出现了一些高可用性的工具如MHA,MMM等解决问题。

在MySQL5.6之后,很简单的解决了这个难题。因为同一事务的GTID在所有节点上的值一致,那么根据server3当前停止点的GTID就能定位到server2上的GTID,所以直接在server3上执行change即可:

mysql> stop slave;Query OK, 0 rows affected (0.02 sec)#千万不要执行 reset master,否则会从最先的GTID上开始执行。mysql> change master to master_host=‘127.0.0.1‘,master_user=‘rep‘,master_password=‘rep‘,master_port=3307,master_auto_position=1; #指定到另一个比较接近主的从上。Query OK, 0 rows affected, 2 warnings (0.02 sec)mysql> start slave; #成功的切换到新主Query OK, 0 rows affected (0.03 sec)

主从结构已经变更,server2是Master,server3是Slave。因为不需要计算pos的值,所以通过GTID很简单的解决了这个问题。

3)跳过复制错误:gtid_next、gtid_purged

① 要是从库执行一个事务错误:

mysql> show slave statusG;*************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 127.0.0.1 Master_User: rep Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin3306.000001 Read_Master_Log_Pos: 38260944 Relay_Log_File: mysqld-relay-bin3307.000002 Relay_Log_Pos: 369 Relay_Master_Log_File: mysql-bin3306.000001 Slave_IO_Running: Yes Slave_SQL_Running: No Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 1008 Last_Error: Error ‘Can‘t drop database ‘mablevi‘; database doesn‘t exist‘ on query. Default database: ‘mablevi‘. Query: ‘drop database mablevi‘ Skip_Counter: 0 Exec_Master_Log_Pos: 151 Relay_Log_Space: 38261371 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: NULLMaster_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 1008 Last_SQL_Error: Error ‘Can‘t drop database ‘mablevi‘; database doesn‘t exist‘ on query. Default database: ‘mablevi‘. Query: ‘drop database mablevi‘ Replicate_Ignore_Server_Ids: Master_Server_Id: 1 Master_UUID: 4e659069-3cd8-11e5-9a49-001c4270714e Master_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: 150810 23:38:39 Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: 4e659069-3cd8-11e5-9a49-001c4270714e:1-48 Executed_Gtid_Set: Auto_Position: 1

在MySQL5.6之前,只需要执行:

mysql> set global sql_slave_skip_counter=1; 

跳过一个错误的事务,就可以继续进行复制了。但在MySQL5.6之后则不行:

mysql> set global sql_slave_skip_counter=1;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来进行复制的,也需要跳过这个事务从而继续复制,这个事务可以到主上的binlog里面查看:因为不知道找哪个GTID上出错,所以也不知道如何跳过哪个GTID。但在show slave status里的信息里可以找到在执行Master里的POS:151

Exec_Master_Log_Pos: 151

的时候报错,所以通过mysqlbinlog找到了GTID:

# at 151#150810 22:57:45 server id 1 end_log_pos 199 CRC32 0x5e14d88f GTID [commit=yes]SET @@SESSION.GTID_NEXT= ‘4e659069-3cd8-11e5-9a49-001c4270714e:1‘/*!*/;

找到这个GTID之后执行:必须按照下面顺序执行

mysql> stop slave;Query OK, 0 rows affected (0.01 sec)mysql> set session gtid_next=‘4e659069-3cd8-11e5-9a49-001c4270714e:1‘; #在session里设置gtid_nextQuery OK, 0 rows affected (0.01 sec)mysql> begin; #开启一个事务Query OK, 0 rows affected (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.01 sec)mysql> SET SESSION GTID_NEXT = AUTOMATIC; #把gtid_next设置回来Query OK, 0 rows affected (0.00 sec)mysql> start slave; #开启复制Query OK, 0 rows affected (0.01 sec)

查看复制状态:

mysql> show slave statusG;*************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 127.0.0.1 Master_User: rep Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin3306.000001 Read_Master_Log_Pos: 38260944 Relay_Log_File: mysqld-relay-bin3307.000003 Relay_Log_Pos: 716 Relay_Master_Log_File: mysql-bin3306.000001 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: 38260944 Relay_Log_Space: 38261936 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: 0Master_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: 4e659069-3cd8-11e5-9a49-001c4270714e 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 the slave I/O thread to update it Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: 4e659069-3cd8-11e5-9a49-001c4270714e:1-48 Executed_Gtid_Set: 4e659069-3cd8-11e5-9a49-001c4270714e:1-48 Auto_Position: 1

在此成功跳过了错误,同步继续。 

注意:通过GTID的复制都是没有指定MASTER_LOG_FILE和MASTER_LOG_POS的,所以通过GTID复制都是从最先开始的事务开始,除非在自己的binlog里面有执行过之前的记录,才会继续后面的执行。

② 要是事务日志被purge,再进行change:

技术分享
mysql> show master logs; +----------------------+-----------+| Log_name | File_size |+----------------------+-----------+| mysql-bin3306.000001 | 38260944 |+----------------------+-----------+1 row in set (0.00 sec)mysql> flush logs;Query OK, 0 rows affected (0.03 sec)mysql> show tables;+---------------+| Tables_in_mmm |+---------------+| patent_family || t1 || t2 |+---------------+3 rows in set (0.01 sec)mysql> create table t3(id int)engine = tokudb;Query OK, 0 rows affected (0.02 sec)mysql> insert into t3 values(3),(4);Query OK, 2 rows affected (0.05 sec)Records: 2 Duplicates: 0 Warnings: 0mysql> flush logs;Query OK, 0 rows affected (0.02 sec)mysql> create table ttt(id int)engine = tokudb;Query OK, 0 rows affected (0.02 sec)mysql> insert into ttt values(1),(2),(3),(4),(5);Query OK, 5 rows affected (0.03 sec)Records: 5 Duplicates: 0 Warnings: 0mysql> show master logs;+----------------------+-----------+| Log_name | File_size |+----------------------+-----------+| mysql-bin3306.000001 | 38260995 || mysql-bin3306.000002 | 656 || mysql-bin3306.000003 | 619 |+----------------------+-----------+3 rows in set (0.00 sec)mysql> purge binary logs to ‘mysql-bin3306.000003‘; #日志被purgeQuery OK, 0 rows affected (0.02 sec)mysql> show master logs; #日志被purge之后等下的binlog +----------------------+-----------+| Log_name | File_size |+----------------------+-----------+| mysql-bin3306.000003 | 619 |+----------------------+--------3308登陆之后执行:mysql> change master to master_host=‘127.0.0.1‘,master_user=‘rep‘,master_password=‘rep‘,master_port=3306,master_auto_position=1;Query OK, 0 rows affected, 2 warnings (0.04 sec)mysql> start slave;Query OK, 0 rows affected (0.02 sec)mysql> show slave statusG;*************************** 1. row *************************** Slave_IO_State: Master_Host: 127.0.0.1 Master_User: rep Master_Port: 3306 Connect_Retry: 60 Master_Log_File: Read_Master_Log_Pos: 4 Relay_Log_File: mysqld-relay-bin3308.000001 Relay_Log_Pos: 4 Relay_Master_Log_File: Slave_IO_Running: No 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: 0 Relay_Log_Space: 151 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: 0Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 1236 Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: ‘The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.‘ Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 1 Master_UUID: 4e659069-3cd8-11e5-9a49-001c4270714e Master_Info_File: /var/lib/mysql3/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: 150811 00:02:50 Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 1
View Code

报错:

 Last_IO_Errno: 1236 Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: ‘The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.‘

这里需要解决的是:Slave如何跳过purge的部分,而不是在最先开始的事务执行。

在主上执行,查看被purge的GTID:mysql> show global variables like ‘gtid_purged‘;+---------------+-------------------------------------------+| Variable_name | Value |+---------------+-------------------------------------------+| gtid_purged | 4e659069-3cd8-11e5-9a49-001c4270714e:1-50 |+---------------+-------------------------------------------+1 row in set (0.00 sec)在从上执行,跳过这个GTID:mysql> stop slave;Query OK, 0 rows affected (0.00 sec)mysql> set global gtid_purged = ‘4e659069-3cd8-11e5-9a49-001c4270714e:1-50‘;Query OK, 0 rows affected (0.02 sec)mysql> reset master;Query OK, 0 rows affected (0.04 sec)mysql> start slave;Query OK, 0 rows affected (0.01 sec)要是出现:ERROR 1840 (HY000): @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.则需要执行:reset master;

到这从的同步就正常了。 

技术分享
mysql> show slave statusG;*************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 127.0.0.1 Master_User: rep Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin3306.000003 Read_Master_Log_Pos: 619 Relay_Log_File: mysqld-relay-bin3308.000002 Relay_Log_Pos: 797 Relay_Master_Log_File: mysql-bin3306.000003 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: 619 Relay_Log_Space: 1006 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: 0Master_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: 4e659069-3cd8-11e5-9a49-001c4270714e Master_Info_File: /var/lib/mysql3/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it Master_Retry_Count: 86400 Master_Bind: Last_IO_E 

小编还为您整理了以下内容,可能对您也有帮助:

mysql 5.6为什么可以做到准确的切换


RDS主节点发生故障,需要进行主备切换时,由于主实例的备节点以及只读实例都是利用MySQL原生复制从主节点异步同步数据,备节点与只读实例之间会有
数据不一致的情况。在发生主备切换后,只读实例将去备节点拉取增量数据,由于Binlog文件中针对变更记录(事务)的变更时间只会到秒级别,针对一秒钟
有大量事物的应用,将无法保证主备切换后备节点与只读实例之间数据一致性。(例如:2014年8月4日 15:33:33
有1万条记录(事务),备节点执行了5000条,2个只读实例分别执行了3400条,8700条)
MySQL5.6版
本针对这一情况推出了GTID的概念,在Binlog文件中的每条变更记录(事务)都会记录一个GTID,当RDS发生主备切换,只读实例切换备节点为主
实例的时候,MySQL原生复制能够根据GTID精确的比对只读实例和备节点的Binlog文件中每条事务的GTID,将只读实例切换至备节点
Binlog文件的正确位置,从而保证主备切换后的数据一致性。

mysql 5.6为什么可以做到准确的切换


RDS主节点发生故障,需要进行主备切换时,由于主实例的备节点以及只读实例都是利用MySQL原生复制从主节点异步同步数据,备节点与只读实例之间会有
数据不一致的情况。在发生主备切换后,只读实例将去备节点拉取增量数据,由于Binlog文件中针对变更记录(事务)的变更时间只会到秒级别,针对一秒钟
有大量事物的应用,将无法保证主备切换后备节点与只读实例之间数据一致性。(例如:2014年8月4日 15:33:33
有1万条记录(事务),备节点执行了5000条,2个只读实例分别执行了3400条,8700条)
MySQL5.6版
本针对这一情况推出了GTID的概念,在Binlog文件中的每条变更记录(事务)都会记录一个GTID,当RDS发生主备切换,只读实例切换备节点为主
实例的时候,MySQL原生复制能够根据GTID精确的比对只读实例和备节点的Binlog文件中每条事务的GTID,将只读实例切换至备节点
Binlog文件的正确位置,从而保证主备切换后的数据一致性。

与MySQL传统复制相比,GTID有哪些独特的复制姿势

前言

GTID(Global Transaction ID)是MySQL5.6引入的功能,可以在集群全局范围标识事务,用于取代过去通过binlog文件偏移量定位复制位置的传统方式。借助GTID,在发生主备切换的情况下,MySQL的其它Slave可以自动在新主上找到正确的复制位置,这大大简化了复杂复制拓扑下集群的维护,也减少了人为设置复制位置发生误操作的风险。另外,基于GTID的复制可以忽略已经执行过的事务,减少了数据发生不一致的风险。

GTID虽好,要想运用自如还需充分了解其原理与特性,特别要注意与传统的基于binlog文件偏移量复制方式不一样的地方。本文概述了关于GTID的几个常见问题,希望能对理解和使用基于GTID的复制有所帮助。

GTID长什么样

根据官方文档定义,GTID由source_id加transaction_id构成。

GTID = source_id:transaction_id

上面的source_id指示发起事务的MySQL实例,值为该实例的server_uuid。server_uuid由MySQL在第一次启动时自动生成并被持久化到auto.cnf文件里,transaction_id是MySQL实例上执行的事务序号,从1开始递增。 例如:

e6954592-8dba-11e6-af0e-fa163e1cf111:1

一组连续的事务可以用‘-‘连接的事务序号范围表示。例如

e6954592-8dba-11e6-af0e-fa163e1cf111:1-5

更一般的情况是GTID的集合。GTID集合可以包含来自多个source_id的事务,它们之间用逗号分隔;如果来自同一source_id的事务序号有多个范围区间,各组范围之间用冒号分隔,例如:

e6954592-8dba-11e6-af0e-fa163e1cf111:1-5:11-18,

e6954592-8dba-11e6-af0e-fa163e1cf3f2:1-27

即,GTID集合拥有如下的形式定义:

gtid_set:

uuid_set [, uuid_set] ...

| ‘‘

uuid_set:

uuid:interval[:interval]...

uuid:

hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh

h:

[0-9|A-F]

interval:

n[-n]

(n >= 1)

如何查看GTID

可以通过MySQL的几个变量查看相关的GTID信息。

gtid_executed

在当前实例上执行过的GTID集合; 实际上包含了所有记录到binlog中的事务。所以,设置set sql_log_bin=0后执行的事务不会生成binlog 事件,也不会被记录到gtid_executed中。执行RESET MASTER可以将该变量置空。

gtid_purged

binlog不可能永远驻留在服务上,需要定期进行清理(通过expire_logs_days可以控制定期清理间隔),否则迟早它会把磁盘用尽。gtid_purged用于记录已经被清除了的binlog事务集合,它是gtid_executed的子集。只有gtid_executed为空时才能手动设置该变量,此时会同时更新gtid_executed为和gtid_purged相同的值。gtid_executed为空意味着要么之前没有启动过基于GTID的复制,要么执行过RESET MASTER。执行RESET MASTER时同样也会把gtid_purged置空,即始终保持gtid_purged是gtid_executed的子集。

gtid_next

会话级变量,指示如何产生下一个GTID。可能的取值如下:

1)AUTOMATIC:

自动生成下一个GTID,实现上是分配一个当前实例上尚未执行过的序号最小的GTID。

2)ANONYMOUS:

设置后执行事务不会产生GTID。

3)显式指定的GTID:

可以指定任意形式合法的GTID值,但不能是当前gtid_executed中的已经包含的GTID,否则,下次执行事务时会报错。

这些变量可以通过show命令查看,比如:

如何产生GTID

GTID的生成受gtid_next控制。 在Master上,gtid_next是默认的AUTOMATIC,即在每次事务提交时自动生成新的GTID。它从当前已执行的GTID集合(即gtid_executed)中,找一个大于0的未使用的最小值作为下个事务GTID。同时在binlog的实际的更新事务事件前面插入一条set gtid_next事件。

以下是一条insert语句生成的binlog记录:

在Slave上回放主库的binlog时,先执行set gtid_next ...,然后再执行真正的insert语句,确保在主和备上这条insert对应于相同的GTID。

一般情况下,GTID集合是连续的,但使用多线程复制(MTS)以及通过gtid_next进行人工干预时会导致gtid空洞。比如下面这样:

继续执行事务,MySQL会分配一个最小的未使用GTID,也就是从出现空洞的地方分配GTID,最终会把空洞填上。

这意味着严格来说我们即不能假设GTID集合是连续的,也不能假定GTID序号大的事务在GTID序号小的事务之后执行,事务的顺序应由事务记录在binlog中的先后顺序决定。

GTID的持久化

GTID相关的信息存储在binlog文件中,为此MySQL5.6新增了下面2个binlog事件。

Previous_gtids_log_event在每个binlog文件的开头部分,记录在该binlog文件之前已执行的GTID集合。

Gtid_log_event即前面看到的set gtid_next ...,它出现在每个事务的前面,表明下一个事务的gtid。

示例如下:

MySQL服务器启动时,通过读binlog文件,初始化gtid_executed和gtid_purged,使它们的值能和上次MySQL运行时一致。

gtid_executed被设置为最新的binlog文件中Previous_gtids_log_event和所有Gtid_log_event的并集。

gtid_purged为最老的binlog文件中Previous_gtids_log_event。

由于这两个重要的变量值记录在binlog中,所以开启gtid_mode时必须同时在主库上开启log_bin在备库上开启log_slave_updates。

但是,在MySQL5.7中没有这个。MySQL5.7中,新增加一个系统表mysql.gtid_executed用于持久化已执行的GTID集合。当主库上没有开启log_bin或在备库上没有开启log_slave_updates时,mysql.gtid_executed会跟用户事务一起每次更新。否则只在binlog日志发生rotation时更新mysql.gtid_executed。

如何配置基于GTID的复制

MySQL服务器的my.cnf配置文件中增加GTID相关的参数

log_bin= /mysql/binlog/mysql_bin

log_slave_updates= true

gtid_mode= ON

enforce_gtid_consistency= true

relay_log_info_repository = TABLE

relay_log_recovery= ON

然后在Slave上指定MASTER_AUTO_POSITION = 1执行CHANGE MASTER TO即可。比如:

CHANGE MASTER TO MASTER_HOST=‘node1‘,MASTER_USER=‘repl‘,MASTER_PASSWORD=‘repl‘,MASTER_AUTO_POSITION=1;

基于GTID的复制如何工作

在MASTER_AUTO_POSITION = 1的情况下 ,MySQL会使用COM_BINLOG_DUMP_GTID协议进行复制。过程如下:

备库发起复制连接时,将自己的已接受和已执行的gtids的并集(后面称为slave_gtid_executed)发送给主库。即下面的集合:

UNION(@@global.gtid_executed, Retrieved_gtid_set - last_received_GTID)

主库将自己的gtid_executed与slave_gtid_executed的差集的binlog发送给Slave。主库的binlog mp过程如下:

检查slave_gtid_executed是否是主库gtid_executed的子集,如否那么主备数据可能不一致,报错。

检查主库的purged_executed是否是slave_gtid_executed的子集,如否代表缺失备库需要的binlog,报错

从最后一个Binlog开始扫描,获取文件头部的PREVIOUS_GTIDS_LOG_EVENT,如果它是slave_gtid_executed的子集,则这是需要发送给Slave的第一个binlog文件,否则继续向前扫描。

从第3步找到的binlog文件的开头读取binlog记录,判断binlog记录是否已被包含在slave_gtid_executed中,如果已包含跳过不发送。

从上面的过程可知,在指定MASTER_AUTO_POSITION = 1时,Master发送哪些binlog记录给Slave,取决于Slave的gtid_executed和Retrieved_Gtid_Set以及Master的gtid_executed,和relay_log_info以及master_log_info中保存的复制位点没有关系。

如何修复复制错误

在基于GTID的复制拓扑中,要想修复Slave的SQL线程错误,过去的SQL_SLAVE_SKIP_COUNTER方式不再适用。需要通过设置gtid_next或gtid_purged完成,当然前提是已经确保主从数据一致,仅仅需要跳过复制错误让复制继续下去。比如下面的场景:

在从库上创建表tb1

mysql> set sql_log_bin=0;

Query OK, 0 rows affected (0.00 sec)

mysql> create table tb1(id int primary key,c1 int);

Query OK, 0 rows affected (1.06 sec)

mysql> set sql_log_bin=1;

Query OK, 0 rows affected (0.00 sec)

在主库上创建表tb1:

mysql> create table tb1(id int primary key,c1 int);

Query OK, 0 rows affected (1.06 sec)

由于从库上这个表已经存在,从库的复制SQL线程出错停止。

上面的输出可以知道,从库已经执行过的事务是‘e10c75be-5c1b-11e6-ab7c-000c296078ae:1-5‘,执行出错的事务是‘e10c75be-5c1b-11e6-ab7c-000c296078ae:6‘,当前主备的数据其实是一致的,可以通过设置gtid_next跳过这个出错的事务。

在从库上执行以下SQL:

mysql> set gtid_next=‘e10c75be-5c1b-11e6-ab7c-000c296078ae:6‘;

Query OK, 0 rows affected (0.00 sec)

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> commit;

Query OK, 0 rows affected (0.00 sec)

mysql> set gtid_next=‘AUTOMATIC‘;

Query OK, 0 rows affected (0.00 sec)

mysql> start slave;

Query OK, 0 rows affected (0.02 sec)

设置gtid_next的方法一次只能跳过一个事务,要批量的跳过事务可以通过设置gtid_purged完成。假设下面的场景:

主库上已执行的事务

从库上已执行的事务

假设经过修复从库已经和主库的数据一致了,但由于复制错误Slave的SQL线程依然处于停止状态。现在可以通过把从库的gtid_purged设置为和主库的gtid_executed一样跳过不一致的GTID使复制继续下去,步骤如下。

在从库上执行

此时从库的Executed_Gtid_Set已经包含了主库上‘1-10‘的事务,再开启复制会从后面的事务开始执行,就不会出错了。

mysql> start slave;

Query OK, 0 rows affected (0.01 sec)

使用gtid_next和gtid_purged修复复制错误的前提是,跳过那些事务后仍可以确保主备数据一致。如果做不到,就要考虑pt-table-sync或者拉备份的方式了。

GTID与备份恢复

在做备份恢复的时候,有时需要恢复出来的MySQL实例可以作为Slave连上原来的主库继续复制,这就要求从备份恢复出来的MySQL实例拥有和数据一致的gtid_executed值。这也是通过设置gtid_purged实现的,下面看下mysqlmp做备份的例子。

1、通过mysqlmp进行备份

通过mysqlmp做一个全量备份:

[root@node1 ~]# mysqlmp --all-databases --single-transaction --routines --events --host=127.0.0.1 --port=3306 --user=root > mp.sql

生成的mp.sql文件里包含了设置gtid_purged的语句

SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;

SET @@SESSION.SQL_LOG_BIN= 0;

...

SET @@GLOBAL.GTID_PURGED=‘e10c75be-5c1b-11e6-ab7c-000c296078ae:1-10‘;

...

SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;

恢复数据前需要先通过reset master清空gtid_executed变量

[root@node2 ~]# mysql -h127.1 -e ‘reset master‘

[root@node2 ~]# mysql -h127.1 <mp.sql

否则执行设置GTID_PURGED的SQL时会报下面的错误

ERROR 1840 (HY000) at line 24: @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.

此时恢复出的MySQL实例的GTID_EXECUTED和备份时点一致:

由于恢复出的MySQL实例已经被设置了正确的GTID_EXECUTED,以master_auto_postion = 1的方式CHANGE MASTER到原来的主节点即可开始复制。

CHANGE MASTER TO MASTER_HOST=‘node1‘, MASTER_USER=‘repl‘, MASTER_PASSWORD=‘repl‘, MASTER_AUTO_POSITION = 1

如果不希望备份文件中生成设置GTID_PURGED的SQL,可以给mysqlmp传入--set-gtid-purged=OFF关闭。

2、通过Xtrabackup进行备份

相比mysqlmp,Xtrabackup是效率更高并且被广泛使用的备份方式。使用Xtrabackup进行备份的举例如下。

通过Xtrabackup创一个全量备份(可以在Slave上创建备份,以避免对主库的性能冲击)

innobackupex --defaults-file=/etc/my.cnf --host=127.1 --user=root --password=mysql --no-timestamp --safe-slave-backup --slave-info /mysql/bak

应用日志

innobackupex --apply-log /mysql/bak

查看备份目录中的xtrabackup_binlog_info文件可以找到备份时已经执行过的gtids

[root@node2 ~]# cat /mysql/bak/xtrabackup_binlog_info

mysql_bin.000001191e10c75be-5c1b-11e6-ab7c-000c296078ae:1-10

由于备份时添加了”--slave-info”选项并且从Slave节点拉取的备份,所以会生成xtrabackup_slave_info文件,也可以从这个文件里查找建立复制的SQL语句。

[root@node2 ~]# cat /mysql/bak/xtrabackup_slave_info

SET GLOBAL gtid_purged=‘e10c75be-5c1b-11e6-ab7c-000c296078ae:1-10‘;

CHANGE MASTER TO MASTER_AUTO_POSITION=1

将备份文件传送到新的节点node3的/mysql/bak目录并恢复(如果直接把备份传输到数据目录了,这一步可以省略)。

[root@node3 ~]# innobackupex --defaults-file=/etc/my.cnf --copy-back /mysql/bak

启动MySQL。

[root@node3 ~]# mysqld --defaults-file=/home/mysql/etc/my.cnf --skip-slave-start &

如果是从Slave拉的备份,一定不能直接开启Slave复制,这时的gtid_executed是错误的。需要手动设置gtid_purged后再start slave

MASTER_HOST=‘node1‘,MASTER_USER=‘repl‘,MASTER_PASSWORD=‘repl‘,MASTER_AUTO_POSITION=1;

start slave;

GTID与MHA

MHA是被广泛使用MySQL HA组件,MHA 0.56以后支持基于GTID的复制。 MHA在failover时会自动判断是否是GTID based failover,需要满足下面3个条件即为GTID based failover

所有节点gtid_mode=1

所有节点Executed_Gtid_Set不为空

至少一个节点Auto_Position=1

和之前的基于binlog文件位置的复制相比,基于GTID复制下,MHA在故障切换时的变化主要如下:

基于binlog文件位置的复制

在Master宕机后会尝试从Master上拷贝binlog日志进行补偿

如果候选Master不拥有最新的relay log,会从拥有最新relay log的Slave上生成差异的binlog传送到候选Master并实施补偿

新Master的日志补偿完成后,同样采用应用差异binlog的方式将其它Slave和新Master同步后再change master到新Master

基于GTID的复制

如果候选Master不拥有最新的relay log,让候选Master连上拥有最新relay log的Salve进行补偿。

尝试从binlog server上拉取缺失的binlog并应用

新Master的数据同步到最新后,让其它的Slave连上新Master并等待数据完成同步。并且可以给masterha_master_switch传入--wait_until_gtid_in_sync=1参数使其不等其它Slave完成数据同步,以加快切换速度。

GTID模式下MHA不会尝试从旧Master上拷贝binlog日志进行补偿,所以在MySQL进程crash而OS仍然健康的情况下,应尽量不要做主备切换而是原地重启MySQL,除非有其

Top