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

本文涉及的产品
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用版 2核4GB 50GB
简介: 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:
    [0-9|A-F]

interval:
    n[-n]

    (n >= 1)

1.1.2 GTID和Binlog的关系

  • GTID在binlog中的结构

binlog_gtid

  • GTID event 结构

gtid_event

  • Previous_gtid_log_event
  1. Previous_gtid_log_event 在每个binlog 头部都会有
  2. 每次binlog rotate的时候存储在binlog头部
  3. Previous-GTIDs在binlog中只会存储在这台机器上执行过的所有binlog,不包括手动设置gtid_purged值。
  4. 换句话说,如果你手动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

1. 假设现在我们要找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相关参数
参数 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 ]

1. gtid_executed = mysql.gtid_executed 【normal】
or
2. 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-b
2. 由于有可能手动设置过gtid_purged=$A:a-b, binlog.index中,first_binlog的Previous-GTIDs肯定不会出现$A:a-b
3. 重置的gtid_purged = @@global.gtid_executed(mysql.gtid_executed:注意,考虑到这个表的更新触发条件,所以这里用@@global.gtid_executed代替) - last_binlog的Previous-GTIDs  - last_binlog所有的gtid_event
4. 下面就用 $reset_gtid_purged 来表示重置的gtid

3)如何持久化gtid_purged [ log-bin=on,log_slave_update=on ]

gtid_purged=binlog.index:first_binlog的Previous-GTIDs  + $reset_gtid_purged

1.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(可选)--高可用切换,最好设置ON
log-slave-updates=ON(可选)--高可用切换,最好设置ON

1.1.5 新的复制协议 COM_BINLOG_DUMP_GTID

  • Slave sends to master range of identifiers of executed transactions to master
  • Master send all other transactions to slave
  • 同样的GTID不能被执行两次,如果有同样的GTID,会自动被skip掉。

com_binlog_dump_gtid

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) Wait until the given GTIDs have executed on slave
  • 新语法
START SLAVE [thread_types] [until_option] [connection_options]

thread_types:
    [thread_type [, thread_type] ... ]

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 的时候停止,下一个事务是11

2. 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 classic replication [运维之痛]

classic_rpl

1.2.2 GTID replication [so easy]

GTID_rpl

1.3 GTID的Limitation

  • 不安全的事务

设置enforce-gtid-consistency=ON

1. CREATE TABLE ... SELECT statements
2. CREATE TEMPORARY TABLE or DROP TEMPORARY TABLE statements inside transactions
3. 同时更新 事务引擎 和 非事务引擎。

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

safe_1

  • 多线程复制

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

safe_multi

二、实战篇(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=off
set 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_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执行

检查错误日志,直到没有错误出现,才能进行下一步

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. master

SHOW MASTER STATUS;

2. 每个slave

SELECT 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(可选)--高可用切换,最好设置ON
log-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上replay
3. 打开新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 运维和错误处理

  1. 使用GTID后,对原来传统的运维有不同之处了,需要调整过来。
  2. 使用Row模式且复制配置正确的情况下,基本上很少发现有复制出错的情况。
  3. slave 设置 super_read_only=on

2.4.1 错误场景: Errant transaction

出现这种问题基本有两种情况

  1. 复制参数没有配置正确,当slave crash后,会出现重复键问题
  2. 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 event
SQL> BEGIN;COMMIT;
SQL> SET GTID_NEXT='AUTOMATIC';
SQL> START SLAVE;

对于第二种不小心多执行了事务

这种情况就比较难了,这样已经导致了数据不一致,大多数情况,建议slave重做
如何避免: slave 设置 super_read_only=on

重点: 当发生inject empty transction后,有可能会丢失事务

这里说下inject empty transction的隐患

  1. 当slave上inject empty transction,说明有一个master的事务被忽略了(这里假设是 $uuid:100)
  2. 事务丢失一:如果此时此刻master挂了,这个slave被选举为新master,那么其他的slave如果还没有执行到$uuid:100,就会丢失掉$uuid:100这个事务。
  3. 事务丢失二:如果从备份中重新搭建一个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还能愉快的过滤掉吗?

可以,改过滤的会自己过滤,不用担心
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
4天前
|
存储 SQL 关系型数据库
深入MySQL锁机制:原理、死锁解决及Java防范技巧
深入MySQL锁机制:原理、死锁解决及Java防范技巧
|
6天前
|
算法 关系型数据库 MySQL
一天五道Java面试题----第七天(mysql索引结构,各自的优劣--------->事务的基本特性和隔离级别)
这篇文章是关于MySQL的面试题总结,包括索引结构的优劣、索引设计原则、MySQL锁的类型、执行计划的解读以及事务的基本特性和隔离级别。
|
11天前
|
关系型数据库 MySQL Linux
【一键解锁神秘力量!】CentOS 7 通过编译源码方式安装 MySQL 数据库 —— 从零到英雄的数据库安装实战秘籍!
【8月更文挑战第9天】随着业务增长,对数据库的需求日益提高。在 CentOS 7 中,通过编译源码安装 MySQL 可提供更高定制性和灵活性。本文详细介绍从准备环境、下载源码、配置编译参数到安装 MySQL 的全过程,并对比 RPM 包安装方法,帮助读者根据需求选择合适方案。实践时需注意备份数据、选择合适版本、确保安全性和调优性能等要点。
54 1
|
15天前
|
存储 关系型数据库 MySQL
MySQL数据库进阶实战:解锁性能飙升秘籍,从菜鸟到高手的华丽蜕变,让数据操作如行云流水!
【8月更文挑战第5天】MySQL是最流行的开源关系型数据库之一,在Web开发与数据分析等领域广泛应用。本文通过实战代码示例,深入探讨MySQL进阶技能:包括索引优化以提升查询性能;利用JOIN与子查询处理多表关联数据;通过事务处理确保数据一致性;使用存储过程与函数封装复杂逻辑以便重用;设置触发器自动执行特定任务以维护数据完整性。掌握这些技能能显著提高数据处理效率与系统性能。
21 5
|
19天前
|
SQL canal 关系型数据库
(二十四)全解MySQL之主从篇:死磕主从复制中数据同步原理与优化
兜兜转转,经过《全解MySQL专栏》前面二十多篇的内容讲解后,基本对MySQL单机模式下的各方面进阶知识做了详细阐述,同时在前面的《分库分表概念篇》、《分库分表隐患篇》两章中也首次提到了数据库的一些高可用方案,但前两章大多属于方法论,并未涵盖真正的实操过程。接下来的内容,会以目前这章作为分割点,开启MySQL高可用方案的落地实践分享的新章程!
127 1
|
19天前
|
SQL 算法 关系型数据库
(二十)MySQL特性篇:2022年的我们,必须要懂的那些数据库新技术!
 MySQL数据库从1995年诞生至今,已经过去了二十多个年头了,到2022.04.26日为止,MySQL8.0.29正式发行了GA版本,在此之前版本也发生了多次迭代,发行了大大小小N多个版本,其中每个版本中都有各自的新特性,所有版本的特性加起来,用一本书的篇幅也无法完全阐述清楚,因此本章主要会挑重点特性来讲,具体各版本的特性可参考MySQL官网的开发手册。
|
20天前
|
SQL 关系型数据库 MySQL
(七)MySQL事务篇:ACID原则、事务隔离级别及事务机制原理剖析
众所周知,MySQL数据库的核心功能就是存储数据,通常是整个业务系统中最重要的一层,可谓是整个系统的“大本营”,因此只要MySQL存在些许隐患问题,对于整个系统而言都是致命的。
|
20天前
|
SQL 算法 关系型数据库
(十)全解MySQL之死锁问题分析、事务隔离与锁机制的底层原理剖析
经过《MySQL锁机制》、《MySQL-MVCC机制》两篇后,咱们已经大致了解MySQL中处理并发事务的手段,不过对于锁机制、MVCC机制都并未与之前说到的《MySQL事务机制》产生关联关系,同时对于MySQL锁机制的实现原理也未曾剖析,因此本篇作为事务、锁、MVCC这三者的汇总篇,会在本章中补全之前空缺的一些细节,同时也会将锁、MVCC机制与事务机制之间的关系彻底理清楚。
|
2天前
|
canal 关系型数据库 MySQL
"揭秘阿里数据同步黑科技Canal:从原理到实战,手把手教你玩转MySQL数据秒级同步,让你的数据处理能力瞬间飙升,成为技术界的新晋网红!"
【8月更文挑战第18天】Canal是一款由阿里巴巴开源的高性能数据同步系统,它通过解析MySQL的增量日志(Binlog),提供低延迟、可靠的数据订阅和消费功能。Canal模拟MySQL Slave与Master间的交互协议来接收并解析Binary Log,支持数据的增量同步。配置简单直观,包括Server和Instance两层配置。在实战中,Canal可用于数据库镜像、实时备份等多种场景,通过集成Canal Client可实现数据的消费和处理,如更新缓存或写入消息队列。
30 0
|
5天前
|
算法 关系型数据库 MySQL
一天五道Java面试题----第七天(mysql索引结构,各自的优劣--------->事务的基本特性和隔离级别)
这篇文章是关于MySQL的面试题总结,包括索引结构的优劣、索引设计原则、MySQL锁的类型、执行计划的解读以及事务的基本特性和隔离级别。