MySQL5.7杀手级新特性:GTID原理与实战
Updated:
- 1. 一、理论篇
- 2. 二、实战篇(how)
- 3. 三、QA
- 4. 四、后续分享: Automatic failover with mysqlfailover+GTID
一、理论篇
1.1 GTID是什么(what)
1.1.1 GTID组成和架构
- GTID 架构
a) GTID = server_uuid:transaction_id
b) server_uuid 来源于 auto.cnf
c) GTID: 在一组复制中,全局唯一
|
|
1.1.2 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文件呢?
|
|
1.1.3 重要参数的持久化
- GTID相关参数
| 参数 | comment |
|---|---|
| gtid_executed | 执行过的所有GTID |
| gtid_purged | 丢弃掉的GTID |
| gtid_mode | gtid模式 |
| gtid_next | session级别的变量,下一个gtid |
| gtid_owned | 正在运行的gtid |
| enforce_gtid_consistency | 保证GTID安全的参数 |
- 重要参数如何持久化
1) 如何持久化gtid_executed [ log-bin=on,log_slave_update=on ]
|
|
2) 如何持久化重置的gtid_purged值?
reset master; set global gtid_purged=$A:a-b;
|
|
3)如何持久化gtid_purged [ log-bin=on,log_slave_update=on ]
|
|
1.1.4 开启GTID的必备条件
- MySQL 5.6
|
|
- MySQL 5.7
MySQL5.7.13 or higher
|
|
1.1.5 新的复制协议 COM_BINLOG_DUMP_GTID
- slave会将已经执行过的gtid,以及以及接受到relay log中的gtid的并集发送给master
|
|
Master send all other transactions to slave
同样的GTID不能被执行两次,如果有同样的GTID,会自动被skip掉。

slave1 : 将自身的UUID1:1 发送给 master,然后接收到了 UUID1:2,UUID1:3 event
slave2 : 将自身的UUID1:1,UUID1:2 发送给 master,然后接收到了UUID1:3 event
1.1.6 GTID重要函数和新语法
- 重要函数
| Name | Description |
|---|---|
| GTID_SUBSET(subset,set) | returns true (1) if all GTIDs in subset are also in set |
| GTID_SUBTRACT(set,subset) | returns only those GTIDs from set that are not in subset |
| WAIT_FOR_EXECUTED_GTID_SET(gtid_set[, timeout]) | Wait until the given GTIDs have executed on slave. |
| WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS(gtid_set[, timeout][,channel]) | Wait until the given GTIDs have executed on slave |
- 新语法
|
|
1.2 GTID有什么好处(why)
1.2.1 classic replication [运维之痛]

1.2.2 GTID replication [so easy]

1.3 GTID的Limitation
- 不安全的事务
设置enforce-gtid-consistency=ON
|
|
1.4 MySQL5.7 GTID crash-safe
http://dev.mysql.com/doc/refman/5.7/en/replication-solutions-unexpected-slave-halt.html
关于crash safe , 可以参考官方文档列出的安全配置
- 单线程复制
Non-GTID 推荐配置: relay_log_recovery=1,relay_log_info_repository=TABLE,master_info_repository=TABLE
GTID 推荐配置:MASTER_AUTO_POSITION=on,relay_log_recovery=0

- 多线程复制
Non-GTID 推荐配置: relay_log_recovery=1, sync_relay_log=1,relay_log_info_repository=TABLE,master_info_repository=TABLE
GTID 推荐配置: MASTER_AUTO_POSITION=on, relay_log_recovery=0

二、实战篇(how)
2.1 使用GTID搭建Replication
2.1.1 从0开始搭建
- step 1: 让所有server处于同一个点
|
|
- step 2: 关闭所有MySQL
|
|
- step 3: 重启所有MySQL,并开启GTID
|
|
当然,在my.cnf中配置好最佳
- step 4: change master
|
|
|
|
- step 5: 让master 可读可写
|
|
2.1.2 从备份中恢复&搭建
- step 1: 备份
|
|
- step 2: 在新服务器上reset master,导入备份
|
|
- step 3: change master
|
|
|
|
2.2 如何从classic replication 升级成 GTID replication
2.2.1 offline 方式升级
offline 的方式升级最简单。全部关机,然后配置好GTID,重启,change master to MASTER_AUTO_POSITION=1。
2.2.2 online 方式升级
这里先介绍几个重要GTID_MODE的value
- GTID_MODE = OFF : 不产生Normal_GTID,只接受来自master的ANONYMOUS_GTID
- GTID_MODE = OFF_PERMISSIVE : 不产生Normal_GTID,可以接受来自master的ANONYMOUS_GTID & Normal_GTID
- GTID_MODE = ON_PERMISSIVE : 产生Normal_GTID,可以接受来自master的ANONYMOUS_GTID & Normal_GTID
GTID_MODE = ON : 产生Normal_GTID,只接受来自master的Normal_GTID
master和slave的gtid_mode 组合搭配矩阵图
水平的GTID_MODE为:master , 垂直的GTID_MODE为:slave
| gtid_mode | OFF(master) | OFF_PERMISSIVE(master) | ON_PERMISSIVE(master) | ON(master) |
|---|---|---|---|---|
| OFF(slave) | Y | Y | N | N |
| OFF_PERMISSIVE(slave) | Y | Y | Y | Y(auto_position可以开启) |
| ON_PERMISSIVE(slave) | Y | Y | Y | Y(auto_position可以开启) |
| ON(slave) | N | N | Y | Y(auto_position可以开启) |
归纳总结:
1) 当master产生Normal_GTID的时候(ON_PERMISSIVE,ON),如果slave的gtid_mode(OFF)不能接受Normal_GTID,那么就会报错
2) 当master产生ANONYMOUS_GTID的时候(OFF_PERMISSIVE,OFF),如果slave的gtid_mode(ON)不能接受ANONYMOUS_GTID,那么就会报错
3) 设置auto_position的条件: 当master gtid_mode=ON时,slave可以为OFF_PERMISSIVE,ON_PERMISSIVE,ON。除此之外,都不能设置auto_position = on
下面罗列下,如何online 升级为GTID模式。
- step 1: 每台server执行
检查错误日志,直到没有错误出现,才能进行下一步
|
|
- step 2: 每台server执行
|
|
- step 3: 每台server执行
不用关心一组复制集群的server的执行顺序,只需要保证每个Server都执行了,才能进行下一步
|
|
- step 4: 每台server执行
不用关心一组复制集群的server的执行顺序,只需要保证每个Server都执行了,才能进行下一步
|
|
- step 5: 在每台server上执行,如果ONGOING_ANONYMOUS_TRANSACTION_COUNT=0就可以
不需要一直为0,只要出现过0一次,就ok
|
|
- step 6: 确保所有anonymous事务传递到slave上了
|
|
- step 7: 每台Server上执行
|
|
- step 8: 在每台server上将my.cnf中添加好gtid配置
|
|
- step 9: change master
|
|
2.3 GTID failover
2.3.1 MySQL crash
配置好loss-less semi-sync replication,可以更可靠的保证数据零丢失。
以下说的都是crash 后,起不来的情况
- binlog 在master还有日志没有传递到 slave
|
|
- binlog 已经传递到slave
|
|
2.3.2 OS crash
|
|
以上操作,在传统模式复制下,只能通过MHA来实现,MHA比较复杂。
现在,在GTID模式下,实现起来非常简单,且非常方便。
2.4 GTID 运维和错误处理
- 使用GTID后,对原来传统的运维有不同之处了,需要调整过来。
- 使用Row模式且复制配置正确的情况下,基本上很少发现有复制出错的情况。
- slave 设置 super_read_only=on
2.4.1 错误场景: Errant transaction
出现这种问题基本有两种情况
- 复制参数没有配置正确,当slave crash后,会出现重复键问题
- DBA操作不正确,不小心在slave上执行了事务
对于第一个重复键问题
- 传统模式
|
|
- GTID模式
|
|
对于第二种不小心多执行了事务
这种情况就比较难了,这样已经导致了数据不一致,大多数情况,建议slave重做
如何避免: slave 设置 super_read_only=on
重点: 当发生inject empty transction后,有可能会丢失事务
这里说下inject empty transction的隐患
- 当slave上inject empty transction,说明有一个master的事务被忽略了(这里假设是 $uuid:100)
- 事务丢失一:如果此时此刻master挂了,这个slave被选举为新master,那么其他的slave如果还没有执行到$uuid:100,就会丢失掉$uuid:100这个事务。
- 事务丢失二:如果从备份中重新搭建一个slave,需要重新执行之前的所有事务,而此时,master挂了, 又回到了事务丢失一的场景。
三、QA
3.1 如何重置gtid_executed,gtid_purged。
- 设置gtid_executed
|
|
- 设置gtid_purged
|
|
3.2 如果auto.cnf 被删掉了,对于GTID的复制会有什么影响?
|
|
3.3 手动设置 set @@gtid_purged = xx:yy, mysql会去主动修改binlog的头么
|
|
3.4 GTID和复制过滤规则之间如何协同工作?MySQL,test还能愉快的过滤掉吗?
|
|
四、后续分享: Automatic failover with mysqlfailover+GTID
http://dev.mysql.com/doc/refman/5.7/en/replication-solutions-switch.html