MySQL GTID全面总结
01 gtid简介
gtid,全称global transaction identifiers,也称之为全局事务id。mysql-5.6.2开始支持,mysql-5.6.10后完善,gtid 分成两部分,一部分是服务的uuid,uuid保存在mysql数据目录的auto.cnf文件中,
这是一个非常重要的文件,不能删除,这一部分是不会变的。下面是一个uuid的值举例:
[root@dev01 mysql]# cat auto.cnf [auto] server-uuid=ac1ebad0-ef76-11e7-872b-080027a03bb6
另外一部分就是事务id了,随着事务的增加,值依次递增。也就是说,gtid实际上是由uuid+tid组成的。其中uuid是一个mysql实例的唯一标识。tid代表了该实例上已经提交的事务数量。如下所示为一个gtid的例子:
3db33b36-0e51-409f-a61d-c99756e90155:1-14
02 gtid工作原理
1、master更新数据时,会在事务前产生gtid,一同记录到binlog日志中。
2、slave端的i/o 线程将变更的binlog,写入到本地的relay log中。
3、sql线程从relay log中获取gtid,然后对比slave端的binlog是否有记录。
4、如果有记录,说明该gtid的事务已经执行,slave会忽略。
5、如果没有记录,slave就会从relay log中执行该gtid的事务,并记录到binlog。
6、在解析过程中会判断是否有主键,如果没有就用二级索引,如果没有就用全部扫描。
03 gtid的优缺点
优点:
1.一个事务对应一个唯一gtid,一个gtid在一个服务器上只会执行一次
2.gtid是用来代替传统复制的方法,gtid复制与普通复制模式的最大不同就是不需要指定二进制文件名和位置
3.减少手工干预和降低服务故障时间,当主机挂了之后通过软件从众多的备机中提升一台备机为主机
缺点:
1.不支持非事务引擎
2.不支持create table ... select 语句复制(主库直接报错)
原理:( 会生成两个sql,一个是ddl创建表sql,一个是insert into 插入数据的sql。
由于ddl会导致自动提交,所以这个sql至少需要两个gtid,但是gtid模式下,只能给这个sql生成一个gtid )
3.不允许一个sql同时更新一个事务引擎表和非事务引擎表
4.开启gtid需要重启(5.7除外)
5.对于create temporary table 和 drop temporary table语句不支持
6.不支持sql_slave_skip_counter
04 测试环境搭建
节点:
server1 192.168.197.128 3306 master
server2 192.168.197.137 3306 slave
server3 192.168.197.136 3306 slave
开启gtid需要启用这三个参数:
gtid_mode = on
enforce_gtid_consistency = 1
log_slave_updates = 1
搭建测试环境的步骤如下:
1.在主节点上创建复制用户,开启主节点的gtid选项;
mysql> grant replication slave on *.* to 'repluser'@'%' identified by '123456'; query ok, 0 rows affected, 1 warning (0.00 sec)
2.从节点上进行change master to操作,搭建主从,如下:
mysql> change master to -> master_host='192.168.197.128', -> master_user='repluser', -> master_password='123456', -> master_port=3306, -> master_auto_position=1; query ok, 0 rows affected, 2 warnings (0.01 sec)
3.搭建成功后,在主节点197.128上查看从节点是否加入:
mysql> show slave hosts; +-----------+------+------+-----------+--------------------------------------+ | server_id | host | port | master_id | slave_uuid | +-----------+------+------+-----------+--------------------------------------+ | 3 | | 3306 | | 969488f5-c486-11e8-adb7-000c29bf2c97 | | 2 | | 3306 | | bb874065-c485-11e8-8b52-000c2934472e | +-----------+------+------+-----------+--------------------------------------+ rows in set (. sec)
查看连接:
mysql> show processlist; +----+----------+------------------+------+------------------+------+---------------------------------------------------------------+------------------+ | id | user | host | db | command | time | state | info | +----+----------+------------------+------+------------------+------+---------------------------------------------------------------+------------------+ | | root | localhost | null | query | 0 | starting | show processlist | | 3 | repluser | work_nat_4:60051 | null | binlog dump gtid | | master has sent all binlog to slave; waiting for more updates | null | | | repluser | work_nat_5: | null | binlog dump gtid | 5970 | master has sent all binlog to slave; waiting for more updates | null | +----+----------+------------------+------+------------------+------+---------------------------------------------------------------+------------------+ rows in set (. sec)
4.三台测试环境的uuid分别是:
197.128 mysql> select @@server_uuid; +--------------------------------------+ | @@server_uuid | +--------------------------------------+ | bd0d-8691-11e8-afd6-4c3e51db5828 | +--------------------------------------+ row in set (0.00 sec) 197.137 mysql> select @@server_uuid; +--------------------------------------+ | @@server_uuid | +--------------------------------------+ | bb874065-c485-11e8-8b52-000c2934472e | +--------------------------------------+ row in set (0.00 sec) 197.136 mysql> select @@server_uuid; +--------------------------------------+ | @@server_uuid | +--------------------------------------+ | f5-c486-11e8-adb7-000c29bf2c97 | +--------------------------------------+ row in set (0.00 sec)
05 开始测试
测试环境主要分为以下几个方面:
a.测试复制的故障转移
b.复制错误跳过
1 测试复制的故障转移
先来看看测试复制的故障转移:
(1)首先将server 3的复制过程停掉
mysql> stop slave; query ok, 0 rows affected (0.01 sec)
(2)在server 1上创建一些数据
mysql> create table yyy.a(id int); query ok, 0 rows affected (0.03 sec) mysql> create table yyy.b(id int); query ok, 0 rows affected (0.02 sec) mysql> create table yyy.c(id int); query ok, 0 rows affected (0.02 sec)
(3)在另外两台上面查看数据结果:
server mysql> show tables from yyy; +---------------+ | tables_in_yyy | +---------------+ | a | | b | | c | +---------------+ rows in set (0.00 sec) server mysql> show tables from yyy; empty set (0.00 sec)
(4)此时可以发现,server 2 的数据相比较server 3,它的数据比较新,此时停止server 1,模拟主服务器宕机:
[root@work_nat_1 init.d]# service mysqld stop shutting down mysql............ [ ok ]
(5)此时我们发现其他两个节点已经不能访问server 1了
mysql> show slave status\g *************************** 1. row *************************** slave_io_state: reconnecting after a failed master event read master_host: 192.168.197.128 master_user: repluser master_port: 3306 connect_retry: 60 master_log_file: mysql-bin.000006 read_master_log_pos: 1364 relay_log_file: mysql-relay-bin.000004 relay_log_pos: 1569 relay_master_log_file: mysql-bin.000006 slave_io_running: connecting slave_sql_running: yes exec_master_log_pos: 1364 relay_log_space: 2337 master_ssl_key: seconds_behind_master: null master_ssl_verify_server_cert: no last_io_errno: 2003 last_io_error: error reconnecting to master 'repluser@192.168.197.128:3306' - retry-time: 60 retries: 1 last_sql_errno: 0
(6)我们需要设置server 2为server 3的主库,因为server 2的数据比较新。此时如果采用以前的办法,需要计算之前主库的log_pos和当前要设置成主库的log_pos,很有可能出错。所以出现了一些高可用性的工具如mha,mmm等解决问题。
在mysql5.6之后,很简单的解决了这个难题。因为同一事务的gtid在所有节点上的值一致,那么根据server3当前停止点的gtid就能定位到server2上的gtid,所以直接在server3上执行change即可:
mysql> change master to -> master_host='192.168.197.137', -> master_user='repluser', -> master_password='123456', -> master_port=, -> master_auto_position=; query ok, rows affected, warnings (0.01 sec)
(7)此时查看server 3上的数据,可以发现,数据已经同步过来了;
2 复制错误跳过
上面的测试中,最终的结果是server 2是主节点,server 3是从节点,下面我们来验证复制错误跳过的办法。
(1)首先我们在从节点上执行一个drop的语句,让两边的数据不一致,如下:
mysql> show databases; +--------------------+ | database | +--------------------+ | information_schema | | dbas | | customer | | inc_db | | mysql | | performance_schema | | sys | | testdb | | yeyz | | yyy | +--------------------+ rows in set (. sec) mysql> drop database yyy; query ok, rows affected (. sec) mysql> show databases; +--------------------+ | database | +--------------------+ | information_schema | | dbas | | customer | | inc_db | | mysql | | performance_schema | | sys | | testdb | | yeyz | +--------------------+ rows in set (. sec)
(2)然后我们在server 2上执行drop database yyy的操作,如下:
mysql> drop database yyy; query ok, 3 rows affected (0.02 sec)
(3)此时我们看到server 3上已经出现了主从不同步的错误警告,因为它上面并没有yyy的数据库(前一步已经删除),错误情况如下;
mysql> show slave status\g *************************** . row *************************** slave_io_state: waiting for master to send event master_host: 192.168.197.137 master_user: repluser master_port: connect_retry: master_log_file: mysql-bin. read_master_log_pos: relay_log_file: mysql-relay-bin. relay_log_pos: relay_master_log_file: mysql-bin. slave_io_running: yes slave_sql_running: no last_errno: last_error: error 'can't drop database 'yyy'; database doesn't exist' on query. default database: 'yyy'. query: 'drop database yyy' skip_counter: exec_master_log_pos: relay_log_space: last_sql_error: error 'can't drop database 'yyy'; database doesn't exist' on query. default database: 'yyy'. query: 'drop database yyy' replicate_ignore_server_ids: master_server_id: master_uuid: bb874065-c485-e8-b52-c2934472e master_info_file: mysql.slave_master_info retrieved_gtid_set: bd0d--e8-afd6-c3e51db5828:-, bb874065-c485-e8-b52-c2934472e: executed_gtid_set: db33b36-e51-f-a61d-c99756e90155:-, bd0d--e8-afd6-c3e51db5828:-, f5-c486-e8-adb7-c29bf2c97: auto_position: replicate_rewrite_db: channel_name: master_tls_version: row in set (0.00 sec)
(4)当我们使用传统的方法来跳过这个错误的时候,会提示出gtid模式下不被允许,如下:
mysql> set global sql_slave_skip_counter=; error (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
那么这种方式下应该如何跳过这个错误呢?
(5)因为我们是通过gtid来进行复制的,也需要跳过这个事务从而继续复制,这个事务可以到主上的binlog里面查看:因为不知道找哪个gtid上出错,所以也不知道如何跳过哪个gtid。但是我们可以在show slave status里的信息里找到在执行master里的pos:2012,也就是上述第(3)步第18行代码。现在我们拿着这个pos:2012去server 2的日志里面找,可以发现如下信息:
# at 2012 #190305 20:59:07 server id 2 end_log_pos 2073 gtid last_committed=9 sequence_number=10 rbr_only=no set @@session.gtid_next= 'bb874065-c485-11e8-8b52-000c2934472e:1'/*!*/; # at 2073 #190305 20:59:07 server id 2 end_log_pos 2158 query thread_id=3 exec_time=0 error_code=0 set timestamp=/*!*/; drop database yyy /*!*/;
(6)我们可以看到gtid_next的值是
,然后我们通过下面的方法来重新恢复主从复制:
mysql> stop slave; query ok, rows affected (0.00 sec) mysql> set session gtid_next='bb874065-c485-11e8-8b52-000c2934472e:1'; query ok, rows affected (0.00 sec) mysql> begin; query ok, rows affected (0.00 sec) mysql> commit; query ok, rows affected (0.01 sec) mysql> set session gtid_next=automatic; query ok, rows affected (0.00 sec) mysql> start slave; query ok, rows affected (0.00 sec) mysql> show slave status\g *************************** 1. row *************************** slave_io_state: waiting for master to send event master_host: 192.168.197.137 master_user: repluser master_port: 3306 connect_retry: 60 master_log_file: mysql-bin.000002 read_master_log_pos: 2158 relay_log_file: mysql-relay-bin.000003 relay_log_pos: 478 relay_master_log_file: mysql-bin.000002 slave_io_running: yes slave_sql_running: yes exec_master_log_pos: 2158 relay_log_space: 1527 until_condition: none master_server_id: 2 master_uuid: bb874065-c485-11e8-8b52-000c2934472e 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: retrieved_gtid_set: bd0d-8691-11e8-afd6-4c3e51db5828:-7, bb874065-c485-11e8-8b52-000c2934472e: executed_gtid_set: db33b36-0e51-409f-a61d-c99756e90155:-14, bd0d-8691-11e8-afd6-4c3e51db5828:-7, f5-c486-11e8-adb7-000c29bf2c97:, bb874065-c485-11e8-8b52-000c2934472e: auto_position: replicate_rewrite_db: channel_name: master_tls_version: row in set (0.00 sec)
以上就是mysql gtid全面总结的详细内容,更多关于mysql gtid的资料请关注其它相关文章!