前面的一篇文章中介绍了基于传统的方式搭建主从复制环境:使用传统的方式打建MYSQL 5.7异步复制环境
一、什么是GTID
GTID是MySQL 5.6的新特性之一,加入了全局事务ID (Global Transaction ID,简称GTID) 来强化数据库的主备一致性,故障恢复,以及容错能力。用于取代传统方式通过binlog以及postion号来定位复制位置的传统方式。借助GTID,在发生主备切换的情况下,MySQL的其它Slave可以自动在新主上找到正确的复制位置,这大大简化了复杂复制拓扑下集群的维护,也减少了人为设置复制位置发生误操作的风险。另外,基于GTID的复制可以忽略已经执行过的事务,减少了数据发生不一致的风险。
那么,GTID长什么样?
根据官方文档定义,GTID由source_id+transaction_id构成。
GTID = source_id:transaction_id
上面的source_id指示发起事务的MySQL实例,值为该实例的server_uuid。server_uuid由MySQL在第一次启动时自动生成并被持久化到auto.cnf文件里,transaction_id是一个从1开始的自增计数,表示在这个主库上执行的第n个事务。MySQL会保证事务与GTID之间的1 : 1映射。例如:
c9eeb85d-de5b-11e8-80d4-525400cf9369:1
表示在以 “c9eeb85d-de5b-11e8-80d4-525400cf9369″为唯一标识的MySQL实例上执行的第1个数据库事务。
很容易理解,MySQL只要保证每台数据库的server_uuid全局唯一,以及每台数据库生成的transaction_id自身唯一,就能保证GTID的全局唯一性。
一组连续的事务可以用’-‘连接的事务序号范围表示。例如:
c9eeb85d-de5b-11e8-80d4-525400cf9369:1-5
更一般的情况是GTID的集合。GTID集合可以包含来自多个source_id的事务,它们之间用逗号分隔;如果来自同一source_id的事务序号有多个范围区间,各组范围之间用冒号分隔,例如:
c9eeb85d-de5b-11e8-80d4-525400cf9369:1-5:11-18,
e6954592-8dba-11e6-af0e-fa163e1cf3f2:1-27
可以使用show master status实时看当前的事务执行数。
那什么是server_uuid?
MySQL 5.6用128位的server_uuid代替了原本的32位server-id的大部分功能。原因很简单,server-id依赖于my.cnf 的手工配置,有可能产生冲突 —— 而自动产生128位uuid的算法可以保证所有的MySQL uuid都不会冲突。
MySQL5.6在数据目录下有一个auto.cnf文件就是用来保存server_uuid的,如下:
[root@VM_54_118_centos ~]# cat /data/mysql/mysql_3306/data/auto.cnf [auto] server-uuid=c9eeb85d-de5b-11e8-80d4-525400cf9369
在 MySQL 再次启动时会读取auto.cnf文件,继续使用上次生成的 server_uuid。使用SHOW命令可以查看MySQL实例当前使用的server_uuid;
root@localhost [(none)]>SHOW GLOBAL VARIABLES LIKE 'server_uuid'; +---------------+--------------------------------------+ | Variable_name | Value | +---------------+--------------------------------------+ | server_uuid | c9eeb85d-de5b-11e8-80d4-525400cf9369 | +---------------+--------------------------------------+ 1 row in set (0.00 sec)
你可以使用基于语句的或基于行的复制与GTID,但是,为了获得最佳效果,生产环境中,建议你使用基于行ROW的格式。
另外支持启用GTID,对运维人员来说应该是一件令人高兴的事情,在配置主从复制,传统的方式里,你需要找到binlog和pos点,然后change master to指向,而不是很有经验的运维,往往会将其找错,造成主从同步复制报错,在MySQL 5.6里,如果使用了GTID,启动一个新的复制从库或切换到一个新的主库,就不必依赖log文件或者pos位。只需要知道master的IP、端口,账号密码即可,因为同步复制是自动的,mysql通过内部机制GTID自动找点同步。
那和传统方式相比,基于GTID的复制有什么优点?
- 在传统的复制里面,当发生故障,需要进行主从切换时,需要找到binlog和pos点,然后change master to指向新的master,相对来说比较麻烦,也容易出错。在MySQL 5.6里面,不用再找binlog和pos点,我们只需要知道master的ip,端口,以及账号密码就行,因为复制是自动的,MySQL会通过内部机制GTID自动找点同步。
- 多线程复制(基于库),在MySQL 5.6以前的版本,slave的复制是单线程的。一个事件一个事件的读取应用。而master是并发写入的,所以延时是避免不了的。唯一有效的方法是把多个库放在多台slave,这样又有点浪费服务器。在MySQL 5.6里面,我们可以把多个表放在多个库,这样就可以使用多线程复制,当只有1个库,多线程复制是没有用的。
那么基于GTID复制实现的工作原理是什么呢?
1)master更新数据时,会在事务前产生GTID,一同记录到binlog日志中。
2)slave端的i/o 线程将变更的binlog,写入到本地的relay log中。
3)sql线程从relay log中获取GTID,然后对比slave端的binlog是否有记录(所以MySQL5.6 SLAVE必须要开启二进制日志记录)。
4)如果有记录,说明该GTID的事务已经执行,slave会忽略。
5)如果没有记录,slave就会从relay log中执行该GTID的事务,并记录到binlog。
6)在解析过程中会判断是否有主键,如果没有就用二级索引,如果没有就用全部扫描。
注意:所以建议每一张表都要创建一个主键,尽量的避免主从延迟的产生;
小结
1)使用master_auto_position=1代替基于binlog和position号的主从复制方式,更便于主从复制的搭建
2)gtid可以知道事务在最开始是在哪个实例上提交的
3)gtid方便实现主从间failover,再也不用不断地找position和binlog了
关于更多基于GTID的详细内容可参考:
https://dev.mysql.com/doc/refman/5.7/en/replication-options-gtids.html
好了,上面介绍了这么多的GTID相关的理论知识,看的都有点累了,下面我们就搭建一个基于GTID方式的主从复制环境;
二、GTID方式搭建主从复制环境
在前面的一篇文章中使用传统方式大家MYSQL 5.7异步复制环境;搭建了基于传统方式的主从环境,下面我们再次基础上,搭建基于GTID方式的主从复制环境
主库:
gtid-mode = ON enforce-gtid-consistency = ON
从库:
gtid-mode = ON enforce-gtid-consistency = ON log-slave-updates = ON
在从库清除原有的主从复制关系
root@localhost [(none)]>stop slave; Query OK, 0 rows affected (0.05 sec) root@localhost [(none)]>reset slave all; Query OK, 0 rows affected (0.28 sec) root@localhost [(none)]>show slave status\G; Empty set (0.01 sec)
关闭主库和备库
[root@wjq1 data]# mysqladmin -u root -pqcloud@2018 shutdown [root@wjq2 data]# mysqladmin -u root -pqcloud@2018 shutdown
重新启动主从
[root@wjq1 data]# mysqld_safe --defaults-file=/etc/my.cnf & [root@wjq2 data]# mysqld_safe --defaults-file=/etc/my.cnf & root@localhost [(none)]>show variables like '%gtid%'; +----------------------------------+-----------+ | Variable_name | Value | +----------------------------------+-----------+ | binlog_gtid_simple_recovery | ON | | enforce_gtid_consistency | ON | | gtid_executed_compression_period | 1000 | | gtid_mode | ON | | gtid_next | AUTOMATIC | | gtid_owned | | | gtid_purged | | | session_track_gtids | OFF | +----------------------------------+-----------+ 8 rows in set (0.16 sec)
建立主从同步关系
root@localhost [(none)]>change master to -> master_host='10.10.1.10', -> master_user='wjqrepl', -> master_password='qcloud@2018', -> master_port=3306, -> master_auto_position=1; Query OK, 0 rows affected, 2 warnings (0.05 sec) root@localhost [(none)]>start slave; Query OK, 0 rows affected (0.02 sec) root@localhost [(none)]>show slave status\G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.10.1.10 Master_User: wjqrepl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql3306.000009 Read_Master_Log_Pos: 436 Relay_Log_File: wjq2-relay-bin.000002 Relay_Log_Pos: 649 Relay_Master_Log_File: mysql3306.000009 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: 436 Relay_Log_Space: 855 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: 330610 Master_UUID: dadc5a32-e0c9-11e8-9dd3-000c29c743b3 Master_Info_File: /data/mysql/3306/data/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: dadc5a32-e0c9-11e8-9dd3-000c29c743b3:1 Executed_Gtid_Set: dadc5a32-e0c9-11e8-9dd3-000c29c743b3:1 Auto_Position: 1 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: 1 row in set (0.00 sec)
如上所述,基于GTID方式的主从复制环境搭建完成;
- 5.7之后,gtid_executed这个值持久化了,在mysql库下新增了一张表:gtid_executed
该表会记录已经执行的gtid集合的信息,有了这张表,就不用再像5.6版本时,必须开启log_slave_updates参数,从库才可以进行复制。gtid信息会保存在gtid_executed表中,可以关闭从库binlog,节约binlog记录开销。执行reset master时,会清空表内所有数据 - 5.7还有gtid_executed_compression_period参数,控制gtid_executed表压缩,默认值为1000,表示执行完1000个事务后开始压缩
- 5.7.6开始,gtid_mode支持动态修改,值有:OFF/OFF_PERMISSIVE/ON_PERMISSIVE/ON
OFF,关闭gtid事务
OFF_PERMISSIVE,新事务是匿名的,同时允许复制的事务可以是gtid,也可以是匿名的
ON_PERMISSIVE,新事务使用gtid,同时允许复制的事务可以是gtid,也可以是匿名的
ON,支持gtid事务
生产环境中,可能有把传统复制改为gtid复制的需求,gtid_mode不支持跳跃式修改
从库上可以通过show slave status获取接收的gtid(retrieve_gtid_set)和执行的gtid(execute_gtid_set)
参数说明:
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。可能的取值如下:
第一个:AUTOMATIC
自动生成下一个GTID,实现上是分配一个当前实例上尚未执行过的序号最小的GTID。
第二个:ANONYMOUS
设置后执行事务不会产生GTID。
第三个:显式指定的GTID
可以指定任意形式合法的GTID值,但不能是当前gtid_executed中的已经包含的GTID,否则,下次执行事务时会报错。