1. MySQL GTID复制
1.1 GTID复制简介
GTID(global transaction identifier)是一个在主数据库上对每个已经提交到数据库的事务的唯一编号,这个标识不仅在主数据库上是唯一的,而且在整个复制架构中的所有数据库中都是唯一的。
一个GTID由一对坐标表示,用冒号(:)分隔:
GTID = source_id : transaction_id
在上面的定义中,每一个GTID均代表一个数据库的事务,等号右边的source_id表示执行事务的源服务器主库的uuid(也就是server_uuid),而transaction_id是一个从1开始的自增的序列号,表示在这个主库上执行的第n个事务。只要保证每台数据库的server_uuid全局唯一,以及每台数据库生成的transaction_id自身唯一,就能保证GTID的全局唯一性。
GTID是事务在数据库中提交时创建分配的唯一标识符,所有事务均与GTID一一映射。下面是一个GTID的具体形式:
2E11FA47-61CA-11E1-9E33-C70AA9429562:28
什么是server_uuid?
MySQL5.6用128位的server_uuid代替了原本32位的server_id的大部分功能。原因很简单,server_id依赖于my.cnf的手工配置,有可能会产生冲突,而自动产生128位uuid的算法可以保证所有的MySQL uuid都不会产生冲突。
在进行首次启动时,MySQL会自动生成一个server_uuid,并且保存到数据库目录下的auto.cnf文件里,这个文件目前存在的唯一目的就是保存server_uuid。在MySQL再次启动时其会读取auto.cnf文件,继续使用上次生成的server_uuid。
1.2 基于GTID复制技术的优缺点及工作原理
1. GTID复制的工作原理简介
GTID复制的工作原理具体如下:
1)当主数据库进行数据更新时,会在事务前产生GTID号,一同记录到binlog日志中。
2)从数据库端的I/O线程将变更的binlog数据,写入到本地的中继日志(relay log)中。
3)从数据库端的SQL线程从中继日志中获取GTID号,然后对比本地的Binlog查看其是否有记录。如果有记录,则说明该GTID的事务已经执行,此时从数据库会忽略。
4)如果没有记录,则从数据库就会从中继日志中获取数据并执行该GTID的事务,并记录到binlog中。
根据GTID号可以知道事务最初是在哪个数据库上提交的,GTID的存在方便了主从复制的宕机切换(failover)。
2. 传统主从复制的宕机切换
由上图可以看到,在MySQL的GTID功能出现以前,主从复制的宕机切换操作过程。
若A服务器宕机,则需要将业务切换到B服务器上。同时,我们又需要使得C服务器与B服务器重新保持复制。这种情况的操作过程命令很简单,即在C服务器上执行“CHANGE MASTER TO MASTER_HOST='xxx',MASTER_LOG_FILE='xxx',MASTER_LOG_POS=nnn”。
问题在于同一个事务在每台数据库服务器上所在的binlog名字和位置点可能都不一样,那么怎样才能找到C当前同步的停止位置点呢,对应B的master_log_file和master_log_pos是什么的时候就变成了难题,这可能是官方推出GTID复制的原因。
其实,master.info文件记录了master_log_file和master_log_pos的位置,可以通过读master.info文件来找到真正的位置点,因此,在没有GTID功能以前,我们也是很快就能够搞定切换的。更进一步的,我们可以通过第三方工具,例如MHA等MySQL集群管理工具来实现主从复制主库宕机的切换以及从库和新主库复制的问题。
不过MySQL在5.6的GTID功能出现以后,对于上述问题的解决似乎变得更简单一些了。由于同一事务的GTID在所有节点上的值都是一致的,那么根据C服务器,当前停止点的GTID就能唯一定位到B上的GTID。而由于MASTER_AUTO_POSITION功能的出现,管理员不需要知道GTID的具体值,直接使用“CHANGE MASTER TO MASTER_HOST='xxx',”MASTER_AUTO_POSITION命令就可以直接完成宕机切换的工作了。
1.3 GTID的优缺点
1. GTID复制的优点
- 根据GTID可以知道事务最初是在哪个数据库上提交的
- GTID对于宕机切换有一定的方便性
2. GTID复制的缺点
- GTID模式实例和非GTID模式实例之间是不能进行复制的,不能混用,要么都是GTID,要么都不是。
- 在同一事物中更新事务表与非事务表将导致多个GTIDs分配给同一事务。官方指出,GTID包含如下三个限制,当enforce-gtid-consistency=ON时,以下三类SQL语句是不支持的:“CREATE TABLE ... SELECT statements” “CREATE TEMPORARY TABLE”和“DROP TEMPORARY TABLE statements inside transactions”
- 同时更新传统事务表和非传统事务表时,有一个例外,如果所有非事务表都是临时表时,非事务的DML被允许在同一事务中或在同一语句中作为传统的DML。
1.4 MySQL GTID复制的应用及实践
1. 环境准备
可以直接使用MySQL半同步复制的环境,修改一下配置文件
2. GTID复制主从库配置文件对比说明
说明:除了server-id之外,其他完全一致。配置完毕后,重启所有的MySQL服务。
3. GTID复制特殊配置参数说明
4. 配置MySQL GTID主从复制
1)建立用于从库复制的帐号及对应权限:
mysql> grant replication slave on *.* to 'rep'@'192.168.150.%' identified by 'oldboy123';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
2)导出主库的数据:
mysqldump -A -B -x --set-gtid-purged=OFF|gzip > /opt/bak1_$(date +%F).sql.gz
3)将主库导出的MySQL数据迁移到从库:
[root@db01 ~]# scp -rp /opt/bak1_$(date +%F).sql.gz root@192.168.150.131:/opt
4)把从主库mysqldump导出的数据恢复到从库
[root@db02 ~]# cd /opt/
[root@db02 opt]# gzip -d bak1_$(date +%F).sql.gz
[root@db02 opt]# mysql < bak1_2019-07-15.sql
5)登录从库(131)配置复制参数
CHANGE MASTER TO
MASTER_HOST='192.168.150.130',
MASTER_PORT=3306,
MASTER_USER='rep',
MASTER_PASSWORD='oldboy123',
MASTER_AUTO_POSITION=1; ---GTID复制的参数
6)启动从库同步开关并测试主从复制
mysql> start slave;
Query OK, 0 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.150.130
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: db01-bin.000003
Read_Master_Log_Pos: 658
Relay_Log_File: db02-relay-bin.000002
Relay_Log_Pos: 405
Relay_Master_Log_File: db01-bin.000003
Slave_IO_Running: Yes ---为Yes就对了
Slave_SQL_Running: Yes ---为Yes就对了
---省略若干---
Exec_Master_Log_Pos: 658
Relay_Log_Space: 608
Until_Condition: None
---省略若干---
Master_UUID: 61dca574-88f8-11e9-a004-000c299a6102
Master_Info_File: /application/mysql-5.6.44/data/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
---省略若干---
Executed_Gtid_Set: 61dca574-88f8-11e9-a004-000c299a6102:1-3,
653f1eca-88f8-11e9-a004-000c29b56ed1:1-129
Auto_Position: 1
1 row in set (0.00 sec)
7)测试主从复制结果。在主库写入数据,然后观察从库的数据状况:
[root@db01 ~]# mysql -e "create database alex_python1;"
[root@db02 ~]# mysql -e "show databases like 'alex%';"
+------------------+
| Database (alex%) |
+------------------+
| alex_python |
| alex_python1 |
+------------------+
根据测试可以判断,从库(192.168.150.131)和主库(192.168.150.130)是同步的,到此,GTID主从复制的基本实践就完成了。
5.5 GTID如何跳过事务冲突
这个功能主要是跳过事务,代替前面的传统复制错误时使用的“set global sql_slave_skip_counter = 1”。
由于在这里GTID必须是连续的,因此正常情况下,同一个服务器产生的GTID是不会存在孔雀的,所以不能简单地skip掉一个事务,只能通过注入空事务的方法替换掉一个实际操作的事务:
stop slave;
set gtid_next='xxx:N';
begin;commit;
set gtid_next='AUTOMAIC';
start slave;
这里的“xxx:N”就是你的slave sql thread报错的GTID号,也即你想要跳过的GTID号。
案例如下:模拟在从库中先建立数据库inca,在主库中继续创建inca,制造冲突。
从库建立数据库inca:
[root@db02 ~]# mysql -e "create database inca;"
主库建立数据库inca:
[root@db01 ~]# mysql -e "create database inca;"
从库:
[root@db02 ~]# mysql -e "show slave status\G"
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.150.130
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: db01-bin.000003
Read_Master_Log_Pos: 966
Relay_Log_File: db02-relay-bin.000002
Relay_Log_Pos: 571
Relay_Master_Log_File: db01-bin.000003
Slave_IO_Running: Yes
Slave_SQL_Running: No
---省略若干---
Last_SQL_Errno: 1007
Last_SQL_Error: Error 'Can't create database 'inca'; database exists' on query. Default database: 'inca'. Query: 'create database inca'
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: 61dca574-88f8-11e9-a004-000c299a6102
Master_Info_File: /application/mysql-5.6.44/data/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: 190715 03:43:48
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set: 61dca574-88f8-11e9-a004-000c299a6102:4-5
Executed_Gtid_Set: 61dca574-88f8-11e9-a004-000c299a6102:1-4,
653f1eca-88f8-11e9-a004-000c29b56ed1:1-130
Auto_Position: 1
故障解决实战:
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)
mysql> set gtid_next='61dca574-88f8-11e9-a004-000c299a6102:5'; ---这是上文“Retrieved_Gtid_Set”里的最大事务号
Query OK, 0 rows affected (0.00 sec)
mysql> begin;commit;
Query OK, 0 rows affected (0.00 sec)
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.01 sec)
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.150.130
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: db01-bin.000003
Read_Master_Log_Pos: 966
Relay_Log_File: db02-relay-bin.000003
Relay_Log_Pos: 445
Relay_Master_Log_File: db01-bin.000003
Slave_IO_Running: Yes ---两个Yes说明修复成功
Slave_SQL_Running: Yes
---省略若干---
Master_UUID: 61dca574-88f8-11e9-a004-000c299a6102
Master_Info_File: /application/mysql-5.6.44/data/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: 61dca574-88f8-11e9-a004-000c299a6102:4-5
Executed_Gtid_Set: 61dca574-88f8-11e9-a004-000c299a6102:1-5,
653f1eca-88f8-11e9-a004-000c29b56ed1:1-130
Auto_Position: 1
1 row in set (0.00 sec)