from:https://yq.aliyun.com/articles/41200
Gtid作为5.6版本以来的杀手级特性,却因为不支持拓扑结构内开关而饱受诟病。如果你需要从未开启GTID的环境升级到开启GTID,需要把这个复制结构里的实例shutdown后,再重启。相信这对于任何24小时服务的互联网应用都是不可接受的。
从5.7.6开始,终于支持在线动态设置gtid_mode和enforce_gtid_consistency了。在介绍如何通过动态设置GTID MODE来开启主备复制结构的GTID之前,我们先介绍几组概念。
匿名事务:对于一个匿名事务,在主库上是不带GTID的,其传递到备库执行也不应该产生GTID。
GTID_MODE:
OFF
|
彻底关闭GTID,如果关闭状态的备库接受到带GTID的事务,则复制中断
|
OFF_PERMISSIVE
|
可以认为是关闭GTID前的过渡阶段,主库在设置成该值后不再生成GTID,备库在接受到带GTID 和不带GTID的事务都可以容忍
主库在关闭GTID时,执行事务会产生一个Anonymous_Gtid事件,会在备库执行:
SET @@SESSION.GTID_NEXT= ‘ANONYMOUS’
备库在执行匿名事务时,就不会去尝试生成本地GTID了
|
ON_PERMISSIVE
|
可以认为是打开GTID前的过渡阶段,主库在设置成该值后会产生GTID,同时备库依然容忍带GTID和不带GTID的事务
|
ON
|
完全打开GTID,如果打开状态的备库接受到不带GTID的事务,则复制中断
|
配置的兼容性矩阵(从worklog上抄的。。):
Master GTID_MODE OFF OFF_PERMISSIVE ON_PERMISSIVE ON Slave GTID_MODE OFF Y Y N N OFF_PERMISSIVE Y Y Y Y* ON_PERMISSIVE Y Y Y Y* ON N N Y Y* N - Slave thread will stop with an error instead of connect Y - GTID_MODEs are compatible * - AUTO_POSITION can be used
GTID_NEXT AUTOMATIC AUTOMATIC ANONYMOUS UUID:NUMBER binlog on binlog off GTID_MODE OFF anonymous anonymous anonymous error OFF_PERMISSIVE anonymous anonymous anonymous UUID:NUMBER ON_PERMISSIVE new GTID anonymous anonymous UUID:NUMBER ON new GTID anonymous error UUID:NUMBER Legend: anonymous - Generate an anonymous transaction. error - Generate an error and fail to execute 'SET GTID_NEXT'. UUID:NUMBER - Generate a GTID with the specified UUID:NUMBER. new GTID - Generate a GTID with an automatically generated number.
可动态配置的ENFORCE_GTID_CONSISTENCY:
WARN
|
在开启GTID之前,可以设置成WARN来观察实例负载中是否存在不兼容GTID的语句,当前包括:
CREATE TABLE ..SELECT
混合非事务和事务引擎的事务
在开启的事务中执行CREATE/DROP TEMPORARY TABLE
设置成WARN时,用户负载能保证正常,错误日志里会记录下来这些语句信息
|
ON
|
打开选项,上述的几种语句将给客户端返回错误。
由于动态设置该选项,我们考虑如下序列,
trx1 begin;
trx1 由于当前选项off,可以执行混合非事务引擎的事务
——另外一个session打开该选项
trx1 commit
为了解决这个问题,在内部检测到GTID不安全的语句时,使用一个计数器统计,只有该值为0时,才允许打开该选项
|
OFF
|
关闭该选项,则无法打开GTID_MODE
|
不管GTID_MODE是否打开还是关闭,总是在ROTATE时产生Previous_gtid_event,其目的是为了防止在反复切换GTID_MODE的过程中,不丢失之前的GTID_EXECUTED和GTID_PURGED。(WL#7592)
CREATE TABLE AS SEELCT这样的SQL,在gtid 打开时,依然不允许执行,但在enforce_gtid_consistency刮泥时, 格式发生了变化,在之前的版本中的binlog格式为:
BEGIN;CREATE TABLE;INSERT;COMMIT;
新的版本修改成了:
Anonymous_Gtid;
CREATE;
Anonymous_Gtid;
BEGIN;INSERT;COMMIT
步骤:
我们来看看如何通过动态设置升级主备。(注意你的复制结构里的实例必须全部先升级到5.7.6及之后的版本)
(你也可以从官方文档http://dev.mysql.com/doc/refman/5.7/en/replication-mode-change-online-enable-gtids.html 来获得开启gtid的流程)
我们假定你的workload 是和Gtid相兼容的,因此跳过设置enforce_gtid_consistency=warn这个步骤以观察不兼容gtid的负载
Step 1:
在每台实例上执行:SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = ON
Step 2:
在每台实例上执行:SET @@GLOBAL.GTID_MODE = OFF_PERMISSIVE
当设成OFF_PERMISSIVE时,备库可以接受任何带GTID或不带GTID的事务,但此时还没有事务拥有GTID
Step 3:
在每台实例上执行:SET @@GLOBAL.GTID_MODE = ON_PERMISSIVE
当设成ON_PERMISSIVE时,主库开始生成GTID,备库依然容忍带GTID和不带GTID的事务。
Step 4:
检查实例上的status变量ONGOING_ANONYMOUS_TRANSACTION_COUNT值为0
假定这种场景:
a.事务设置GTID_NEXT=‘ANONYMOUS’,然后执行事务,这是允许的,因为当前GITD_MODE为ON_PERMISSIVE
b.另外一个session设置GTID_MODE = ON;
c.事务提交
这可能产生不一致的状态,即打开GTID后,依然存在匿名事务,因此在内部维持了一个计数器ONGOING_ANONYMOUS_TRANSACTION_COUNT来维持匿名事务的数量,只有这个值为0时,再去设置GIT_MODE为ON
Step 5:
等待所有的复制完成,没有延迟,做一次flush logs,然后将前面的日志purge掉, 这些日志包含带GTID和不带GTID的事务。
Step 6:
在每台实例上执行:SET @@GLOBAL.GTID_MODE = ON
当然这些配置需要写入配置文件中,防止重启后丢失
禁止GTID的步骤(官方文档:http://dev.mysql.com/doc/refman/5.7/en/replication-mode-change-online-disable-gtids.html)
基本上和开启的步骤是互逆的。
Step 1:
通过change master关闭MASTER_AUTO_POSITION
Step 2:
在每台实例上执行:
SET @@GLOBAL.GTID_MODE = ON_PERMISSIVE;
Step 3:
在每台实例上执行:
SET @@GLOBAL.GTID_MODE = OFF_PERMISSIVE;
Step 4:
确认所有的gtid_owned集合为空,通过show variables查看
Step5:
等待当前日志已完全被备库复制完成,关闭GTID,设置GTID_MODE=OFF
同样的,这些混合了GTID和非GTID的事务也需要被purge掉。
关于如何确认匿名事务(没有gtid的事务)被完全执行完成,参考文档http://dev.mysql.com/doc/refman/5.7/en/replication-mode-change-online-verify-transactions.html
另外强烈推荐阅读worklog:http://dev.mysql.com/worklog/task/?id=7083 在这个worklog中以大篇幅的字数介绍了所有可能遇到的情况及处理策略。。