GTID 主从
GTID 概念介绍
GTID 即全局事务ID(global transaction identifier), 其保证为每一个在主上提交的事务在复制集群中可以生成一个唯一的 ID 。GTID 最初由 google 实现,官方 MySQL 在 5.6 才加入该功能。mysql 主从结构在一主一从情况下对于 GTID 来说就没有优势了,而对于 2 台主以上的结构优势异常明显,可以在数据不丢失的情况下切换新主。使用 GTID 需要注意: 在构建主从复制之前,在一台将成为主的实例上进行一些操作(如数据清理等),通过 GTID 复制,这些在主从成立之前的操作也会被复制到从服务器上,引起复制失败。也就是说通过 GTID 复制都是从最先开始的事务日志开始,即使这些操作在复制之前执行。比如在 server1 上执行一些 drop、delete 的清理操作,接着在 server2 上执行 change 的操作,会使得 server2 也进行 server1 的清理操作。
GTID 实际上是由 UUID + TID(即 transactionId)组成的。其中 UUID(即server_uuid) 产生于 auto.conf 文件(cat /data/mysql/data/auto.cnf),是一个 MySQL 实例的唯一标识。TID 代表了该实例上已经提交的事务数量,并且随着事务提交单调递增,所以 GTID 能够保证每个 MySQL 实例事务的执行(不会重复执行同一个事务,并且会补全没有执行的事务)。GTID 在一组复制中,全局唯一。下面是一个 GTID 的具体形式 :
mysql> show master status;
+-----------+----------+--------------+------------------+-------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------+----------+--------------+------------------+-------------------------------------------+
| on.000003 | 187 | | | 7286f791-125d-11e9-9a9c-0050568843f8:1-362|
+-----------+----------+--------------+------------------+-------------------------------------------+
1 row in set (0.00 sec)
GTID:7286f791-125d-11e9-9a9c-0050568843f8:1-362
UUID:7286f791-125d-11e9-9a9c-0050568843f8
transactionId:1-362
在整个复制架构中GTID 是不变化的,即使在多个连环主从中也不会变。
例如:ServerA --->ServerB ---->ServerC
GTID从在ServerA ,ServerB,ServerC 中都是一样的。
了解了 GTID 的格式,通过 UUID 可以知道这个事务在哪个实例上提交的。通过 GTID 可以极方便的进行复制结构上的故障转移,新主设置,这就很好地解决了下面这个图所展现出来的问题。
如图, Server1(Maste) 崩溃,根据从上 show slave status 获得 Master_log_File/Read_Master_Log_Pos的值,Server2(Slave) 已经跟上了主,Server3(Slave) 没有跟上主。这时要是把 Server2 提升为主,Server3 变成 Server2 的从。这时在 Server3 上执行 change 的时候需要做一些计算。
这个问题在 5.6 的 GTID 出现后,就显得非常的简单。由于同一事务的 GTID 在所有节点上的值一致,那么根据 Server3 当前停止点的 GTID 就能定位到 Server2 上的 GTID 。甚至由于 MASTER_AUTO_POSITION 功能的出现,我们都不需要知道 GTID 的具体值,直接使用 CHANGE MASTER TO MASTER_HOST=‘xxx’, MASTER_AUTO_POSITION 命令就可以直接完成 failover 的工作。
====== GTID和Binlog的关系 ======
-
GTID 在 binlog 中的结构 -
GTID event 结构 -
Previous_gtid_log_event Previous_gtid_log_event 在每个 binlog 头部都会有每次 binlog rotate 的时候存储在 binlog 头部 Previous-GTIDs 在 binlog 中只会存储在这台机器上执行过的所有 binlog ,不包括手动设置 gtid_purged 值。换句话说,如果你手动 set global gtid_purged=xx; 那么 xx 是不会记录在 Previous_gtid_log_event 中的。 -
GTID 和 Binlog 之间的关系是怎么对应的呢? 如何才能找到 GTID=? 对应的 binlog 文件呢? 假设有4个 binlog: bin.001,bin.002,bin.003,bin.004 bin.001 : Previous-GTIDs=empty; binlog_event有: 1-40 bin.002 : Previous-GTIDs=1-40; binlog_event有: 41-80 bin.003 : Previous-GTIDs=1-80; binlog_event有: 81-120 bin.004 : Previous-GTIDs=1-120; binlog_event有: 121-160 假设现在我们要找 GTID=$A,那么 MySQL 的扫描顺序为: -
从最后一个 binlog 开始扫描(即: bin.004) -
bin.004 的 Previous-GTIDs=1-120,如果 $A=140 > Previous-GTIDs,那么肯定在bin.004 中 -
bin.004 的 Previous-GTIDs=1-120,如果 $A=88 包含在 Previous-GTIDs 中,那么继续对比上一个 binlog 文件 bin.003,然后再循环前面2个步骤,直到找到为止.
====== GTID 重要参数的持久化======
GTID 相关参数:
参数 | comment |
---|
gtid_executed | 执行过的所有 GTID | gtid_purged | 丢弃掉的GTID | gtid_mode | GTID模式 | gtid_next | session级别的变量,下一个gtid | gtid_owned | 正在运行的GTID | enforce_gtid_consistency | 保证GTID安全的参数 |
====== 开启GTID的必备条件======
gtid_mode=on (必选)
enforce-gtid-consistency=1 (必选)
log_bin=mysql-bin (可选) #高可用切换,最好开启该功能
log-slave-updates=1 (可选) #高可用切换,最好打开该功能
GTID 工作原理
从服务器连接到主服务器之后,把自己执行过的 GTID(Executed_Gtid_Set:即已经执行的事务编码)、获取到的 GTID(Retrieved_Gtid_Set:即从库已经接收到主库的事务编号)发给主服务器,主服务器把从服务器缺少的 GTID 及对应的 transactions 发过去补全即可。当主服务器挂掉的时候,找出同步最成功的那台从服务器,直接把它提升为主即可。如果硬要指定某一台不是最新的从服务器提升为主, 先 change 到同步最成功的那台从服务器, 等把 GTID 全部补全了,就可以把它提升为主了。
GTID 是 MySQL 5.6 的新特性,可简化 MySQL 的主从切换以及 Failover 。GTID 用于在 binlog 中唯一标识一个事务。当事务提交时,MySQL Server 在写 binlog 的时候,会先写一个特殊的 Binlog Event ,类型为 GTID_Event ,指定下一个事务的 GTID ,然后再写事务的 Binlog 。主从同步时 GTID_Event 和事务的 Binlog 都会传递到从库,从库在执行的时候也是用同样的 GTID 写 binlog ,这样主从同步以后,就可通过 GTID 确定从库同步到的位置了。也就是说,无论是级联情况,还是一主多从情况,都可以通过 GTID 自动找点儿,而无需像之前那样通过 File_name 和 File_position 找点儿了。
简而言之,GTID 的工作流程为:
- master 更新数据时,会在事务前产生 GTID ,一同记录到 binlog 日志中。
- slave 端的 i/o 线程将变更的 binlog ,写入到本地的 relay log 中。
- sql 线程从 relay log 中获取 GTID ,然后对比 slave 端的 binlog 是否有记录。
- 如果有记录,说明该 GTID 的事务已经执行,slave 会忽略。
- 如果没有记录,slave 就会从 relay log 中执行该 GTID 的事务,并记录到 binlog 。
- 在解析过程中会判断是否有主键,如果没有就用二级索引,如果没有就用全部扫描。
GTID 主从配置
- 主库配置。vi /etc/my.cnf,添加以下配置,重启 mysql,关闭防火墙。
log-bin=128
server-id=10
gtid_mode=on
enforce-gtid-consistency=true
log-slave-updates=on
[root@128 ~]# systemctl restart mysqld
[root@128 ~]# systemctl stop firewalld
[root@128 ~]# setenforce 0
- 从库配置。vi /etc/my.cnf,添加以下配置,重启 mysql,关闭防火墙。
server-id=20
relay-log=myrelay
gtid_mode=on
enforce-gtid-consistency=true
log-slave-updates=on
read_only=on
master-info-repository=TABLE
relay-log-info-repository=TABLE
[root@131 ~]# systemctl restart mysqld
[root@131 ~]# systemctl stop firewalld
[root@131 ~]# setenforce 0
- 主库授权复制用户。
mysql> grant replication slave on *.* to 'abc'@'%' identified by '123456';
Query OK, 0 rows affected, 1 warning (0.02 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> show master status;
+------------+----------+--------------+------------------+------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------+----------+--------------+------------------+------------------------------------------+
| 128.000001 | 588 | | | 95cb821b-0cca-11ed-8584-000c295e7eb4:1-2 |
+------------+----------+--------------+------------------+------------------------------------------+
1 row in set (0.00 sec)
- 从库设置要同步的主库信息,并开启同步。
mysql> change master to master_host='192.168.91.128',master_port=3306,master_user='abc',master_password='123456',master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.02 sec)
mysql> start slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show slave status \G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.91.128
Master_User: abc
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: 128.000001
Read_Master_Log_Pos: 588
Relay_Log_File: myrelay.000002
Relay_Log_Pos: 789
Relay_Master_Log_File: 128.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
.................................
配置完之后,通过查看 slave 的状态,可以看是否配置成功。同时可以在主库进行一些操作,提交一些事务(insert,update),之后数据就会自动同步到从库。
|