qazxsw1 发表于 2018-10-5 14:09:19

MySQL5.7杀手级新特性:GTID原理与实战

MySQL5.7杀手级新特性:GTID原理与实战
  摘要: MySQL5.7杀手级新特性:GTID原理与实战
一、理论篇
1.1 GTID是什么(what)
1.1.1 GTID组成和架构
GTID 架构
a) GTID =server_uuid:transaction_id
b) server_uuid 来源于 auto.cnf
c) G
MySQL5.7杀手级新特性:GTID原理与实战
一、理论篇
1.1 GTID是什么(what)
1.1.1 GTID组成和架构

[*]  GTID 架构
  a) GTID =server_uuid:transaction_id
  b) server_uuid 来源于 auto.cnf
  c) GTID: 在一组复制中,全局唯一
gtid_set:  
    uuid_set [, uuid_set] ...
  
    | ''uuid_set:
  
    uuid:interval[:interval]...
  

  
uuid:
  
    hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh
  

  
h:
  
   
  

  
interval:
  
    n[-n]
  

  
    (n >= 1)
1.1.2 GTID和Binlog的关系

[*]  GTID在binlog中的结构
https://oss-cn-hangzhou.aliyuncs.com/yqfiles/cde150528e0f8f525fea5edef2c42c129813481c.jpeg

[*]  GTID event 结构
https://oss-cn-hangzhou.aliyuncs.com/yqfiles/cac6a907ba868eb7a523b405771e8dfdde31f7fd.jpeg

[*]  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-1601.
  
假设现在我们要找GTID=$A,那么MySQL的扫描顺序为:
  
从最后一个binlog开始扫描(即:bin.004)2. bin.004的Previous-GTIDs=1-120,如果$A=140 > Previous-GTIDs,
  
那么肯定在bin.004中3. bin.004的Previous-GTIDs=1-120,如果$A=88 包含在Previous-GTIDs中,那么继续对比上一个binlog文件
  
bin.003,然后再循环前面2个步骤,直到找到为止
1.1.3 重要参数的持久化

[*]  GTID相关参数
参数commentgtid_executed执行过的所有GTIDgtid_purged丢弃掉的GTIDgtid_modegtid模式gtid_nextsession级别的变量,下一个gtidgtid_owned正在运行的gtidenforce_gtid_consistency保证GTID安全的参数

[*]  重要参数如何持久化
  1) 如何持久化gtid_executed[ log-bin=on,log_slave_update=on ]
1. gtid_executed = mysql.gtid_executed 【normal】  
or2. gtid_executed = mysql.gtid_executed +last_binlog中最后没写到mysql.gtid_executed中的gtid_event【recover】
  2) 如何持久化重置的gtid_purged值?
  reset master; set global gtid_purged=$A:a-b;
1. 由于有可能手动设置过gtid_purged=$A:a-b, binlog.index中,last_binlog的Previous-GTIDs并不会包含$A:a-b2.  
    由于有可能手动设置过gtid_purged=$A:a-b, binlog.index中,first_binlog的Previous-GTIDs肯定不会出现$A:a-b3.
  
    重置的gtid_purged = @@global.gtid_executed(mysql.gtid_executed:注意,考虑到这个表的更新触发条件,
  
    所以这里用@@global.gtid_executed代替) - last_binlog的Previous-GTIDs- last_binlog所有的gtid_event4.
  
    下面就用 $reset_gtid_purged 来表示重置的gtid
  3)如何持久化gtid_purged [ log-bin=on,log_slave_update=on ]
gtid_purged=binlog.index:first_binlog的Previous-GTIDs+ $reset_gtid_purged1.1.4 开启GTID的必备条件

[*]  MySQL 5.6
gtid_mode=ON(必选)log_bin=ON(必选)log-slave-updates=ON(必选)enforce-gtid-consistency(必选)

[*]  MySQL 5.7
  MySQL5.7.13 or higher
gtid_mode=ON(必选)enforce-gtid-consistency(必选)log_bin=ON(可选)--高可用切换,最好设置ONlog-slave-updates=ON(可选)--高可用切换,最好设置ON1.1.5 新的复制协议COM_BINLOG_DUMP_GTID

[*]  Slave sends to master range of>
[*]  Master send all other transactions to slave
[*]  同样的GTID不能被执行两次,如果有同样的GTID,会自动被skip掉。
https://oss-cn-hangzhou.aliyuncs.com/yqfiles/cdf68f4825178fde75299e277d0887b99c70e8f1.jpeg
  slave1 : 将自身的UUID1:1 发送给 master,然后接收到了 UUID1:2,UUID1:3 event
  slave2 : 将自身的UUID1:1,UUID1:2 发送给 master,然后接收到了UUID1:3 event
1.1.6 GTID重要函数和新语法

[*]  重要函数
NameDescriptionGTID_SUBSET(subset,set)returns true (1) if all GTIDs in subset are also in setGTID_SUBTRACT(set,subset)returns only those GTIDs from set that are not in subsetWAIT_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)Wait until the given GTIDs have executed on slave

[*]  新语法
START SLAVE   

  
thread_types:
  
    ... ]
  

  
thread_type:
  
    IO_THREAD | SQL_THREAD
  

  
until_option:    UNTIL {   {SQL_BEFORE_GTIDS | SQL_AFTER_GTIDS} = gtid_set
  
          |   MASTER_LOG_FILE = 'log_name', MASTER_LOG_POS = log_pos
  
          |   RELAY_LOG_FILE = 'log_name', RELAY_LOG_POS = log_pos
  
          |   SQL_AFTER_MTS_GAPS}
  

  

  
* 举个栗子:1. START SLAVE SQL_THREAD UNTIL SQL_BEFORE_GTIDS = 3E11FA47-71CA-11E1-9E33-C80AA9429562:11-56表示,
  
当SQL_thread 执行到3E11FA47-71CA-11E1-9E33-C80AA9429562:10 的时候停止,
  
下一个事务是112. START SLAVE SQL_THREAD UNTIL SQL_AFTER_GTIDS = 3E11FA47-71CA-11E1-9E33-C80AA9429562:11-56表示,
  
当SQL_thread 执行到3E11FA47-71CA-11E1-9E33-C80AA9429562:56 的时候停止,56是最后一个提交的事务。
1.2 GTID有什么好处(why)
1.2.1>https://oss-cn-hangzhou.aliyuncs.com/yqfiles/35bde6d750bd6fff3e1bb9369846daa5714e63de.jpeg
1.2.2 GTID replication
https://oss-cn-hangzhou.aliyuncs.com/yqfiles/be6ee66e18a0e31c9d2e8b323453fa46c635fc1a.jpeg
1.3 GTID的Limitation

[*]  不安全的事务
  设置enforce-gtid-consistency=ON
1. CREATE TABLE ... SELECT statements2. CREATE TEMPORARY TABLE or DROP TEMPORARY TABLE statements inside transactions3.  
同时更新 事务引擎 和 非事务引擎。
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 推荐配置:>  GTID 推荐配置:MASTER_AUTO_POSITION=on,relay_log_recovery=0
https://oss-cn-hangzhou.aliyuncs.com/yqfiles/650e1908f614ec8a7c19fb1b5ac741677c063e98.jpeg

[*]  多线程复制

  Non-GTID 推荐配置:>
  GTID 推荐配置: MASTER_AUTO_POSITION=on,>https://oss-cn-hangzhou.aliyuncs.com/yqfiles/cf238193adb168079783e06d9fb3990b6abfcc5c.jpeg
二、实战篇(how)
2.1 使用GTID搭建Replication
2.1.1 从0开始搭建

[*]  step 1: 让所有server处于同一个点
mysql> SET @@global.read_only = ON;

[*]  step 2: 关闭所有MySQL
shell> mysqladmin -uusername -p shutdown

[*]  step 3: 重启所有MySQL,并开启GTID
shell> mysqld --gtid-mode=ON --log-bin --enforce-gtid-consistency &  当然,在my.cnf中配置好最佳

[*]  step 4: change master
mysql> CHANGE MASTER TO  
   >   MASTER_HOST = host,
  
   >   MASTER_PORT = port,
  
   >   MASTER_USER = user,
  
   >   MASTER_PASSWORD = password,
  
   >   MASTER_AUTO_POSITION = 1;
mysql> START SLAVE;

[*]  step 5: 让master 可读可写
mysql> SET @@global.read_only = OFF;2.1.2 从备份中恢复&搭建

[*]  step 1: 备份
mysqldump xx 获取并且记录gtid_purged值or冷备份 --获取并且记录gtid_executed值,这个就相当于mysqldump中得到的gtid_purged

[*]  step 2: 在新服务器上reset master,导入备份
reset master; --清空gtid信息导入备份; --如果是逻辑导入,请设置sql_log_bin=offset global gtid_purged=xx;

[*]  step 3: change master
mysql> CHANGE MASTER TO  
   >   MASTER_HOST = host,
  
   >   MASTER_PORT = port,
  
   >   MASTER_USER = user,
  
   >   MASTER_PASSWORD = password,
  
   >   MASTER_AUTO_POSITION = 1;
mysql> START SLAVE;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_modeOFF(master)OFF_PERMISSIVE(master)ON_PERMISSIVE(master)ON(master)OFF(slave)YYNNOFF_PERMISSIVE(slave)YYYY(auto_position可以开启)ON_PERMISSIVE(slave)YYYY(auto_position可以开启)ON(slave)NNYY(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执行
  检查错误日志,直到没有错误出现,才能进行下一步
SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = WARN;

[*]  step 2: 每台server执行
SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = ON;

[*]  step 3: 每台server执行
  不用关心一组复制集群的server的执行顺序,只需要保证每个Server都执行了,才能进行下一步
SET @@GLOBAL.GTID_MODE = OFF_PERMISSIVE;

[*]  step 4: 每台server执行
  不用关心一组复制集群的server的执行顺序,只需要保证每个Server都执行了,才能进行下一步
SET @@GLOBAL.GTID_MODE = ON_PERMISSIVE;

[*]  step 5: 在每台server上执行,如果ONGOING_ANONYMOUS_TRANSACTION_COUNT=0就可以
  不需要一直为0,只要出现过0一次,就ok
SHOW STATUS LIKE 'ONGOING_ANONYMOUS_TRANSACTION_COUNT';

[*]  step 6: 确保所有anonymous事务传递到slave上了
1. masterSHOW MASTER STATUS;  

  
2. 每个slaveSELECT MASTER_POS_WAIT(file, position);
  

  

  
或者,等一段时间,只要不是大的延迟,一般都没问题

[*]  step 7: 每台Server上执行
SET @@GLOBAL.GTID_MODE = ON;

[*]  step 8: 在每台server上将my.cnf中添加好gtid配置
gtid_mode=ON(必选)enforce-gtid-consistency(必选)log_bin=ON(可选)--高可用切换,最好设置ONlog-slave-updates=ON(可选)--高可用切换,最好设置ON

[*]  step 9: change master
STOP SLAVE;CHANGE MASTER TO MASTER_AUTO_POSITION = 1;START SLAVE;2.3 GTID failover
2.3.1 MySQL crash
  配置好loss-less semi-sync replication,可以更可靠的保证数据零丢失。
  以下说的都是crash 后,起不来的情况


[*]  binlog 在master还有日志没有传递到 slave
1. 选取最新的slave,change master to maseter_auto_position同步好2. mysqlbinlog 将没传递过来的binlog在新master上replay3.  
    打开新master的surper_read_only=off;

[*]  binlog 已经传递到slave
1. 选取最新的slave,change master to maseter_auto_position同步好2. 打开新master的surper_read_only=off;2.3.2 OS crash
1. 选取最新的slave,change master to maseter_auto_position同步好2. 打开新master的surper_read_only=off;  以上操作,在传统模式复制下,只能通过MHA来实现,MHA比较复杂。
  现在,在GTID模式下,实现起来非常简单,且非常方便。
2.4 GTID 运维和错误处理

[*]  使用GTID后,对原来传统的运维有不同之处了,需要调整过来。
[*]  使用Row模式且复制配置正确的情况下,基本上很少发现有复制出错的情况。
[*]  slave 设置 super_read_only=on
2.4.1 错误场景: Errant transaction
  出现这种问题基本有两种情况

[*]  复制参数没有配置正确,当slave crash后,会出现重复键问题
[*]  DBA操作不正确,不小心在slave上执行了事务
  对于第一个重复键问题

[*]  传统模式
* skip transation;  

  
SQL> SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
  
SQL> START SLAVE;

[*]  GTID模式
SQL> SET GTID_NEXT='b9b4712a-df64-11e3-b391-60672090eb04:7';   --设置需要跳过的gtid eventSQL> BEGIN;COMMIT;  
SQL> SET GTID_NEXT='AUTOMATIC';
  
SQL> START SLAVE;
  对于第二种不小心多执行了事务
  这种情况就比较难了,这样已经导致了数据不一致,大多数情况,建议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
目前只能够reset master

[*]  设置gtid_purged
* 当gtid_executed 非空的时候,不能设置gtid_purged  
* 当gtid_executed 为空的时候(即刚刚备份好的镜像,刚搭建的mysql),可以直接SET @@GLOBAL.GTID_PURGED='0ad6eae9-2d66-11e6-864f-ecf4bbf1f42c:1-3';
3.2 如果auto.cnf 被删掉了,对于GTID的复制会有什么影响?
如果被删掉,重启后,server-uuid 会变3.3 手动设置 set @@gtid_purged = xx:yy, mysql会去主动修改binlog的头么
不会3.4 GTID和复制过滤规则之间如何协同工作?MySQL,test还能愉快的过滤掉吗?
可以,改过滤的会自己过滤,不用担心

页: [1]
查看完整版本: MySQL5.7杀手级新特性:GTID原理与实战