1. GTID的格式和存储

GTID即全局事务ID(global transaction identifier),GTID实际上是由server_uuid:transaction_id组成的。其中server_uuid是一个MySQL实例的唯一标识,存放在数据目录的auto.cnf文件下,transaction_id代表了该实例上已经提交的事务数量,并且随着事务提交单调递增,所以GTID能够保证每个MySQL实例事务的执行(不会重复执行同一个事务,并且会补全没有执行的事务)。

1.1 GTID 集

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)

GTID集在MySQL服务器中以多种方式使用。 例如,gtid_executedgtid_purged系统变量存储的值表示为GTID集。 此外,函数GTID_SUBSET()GTID_SUBTRACT()需要GTID集作为输入。 当从服务器变量返回GTID集时,UUID按字母顺序排列,数值间隔按升序合并。

1.2 mysql.gtid_executed 表

GTID存储在mysql数据库中名为gtid_executed的表中。 对于它表示的每个GTID或GTID集合,该表中的一行包含原始服务器的UUID,以及该集合的起始和结束事务ID; 对于仅引用单个GTID的行,这两个最后两个值是相同的。

当slave禁用binlog时,mysql.gtid_executed表使slave能够使用GTID,并且它可以在二进制日志丢失时保留GTID历史记录。

当gtid_mode为ON或ON_PERMISSIVE时,GTID仅存储在mysql.gtid_executed表中,存储GTID的位置取决于是启用还是禁用binlog:

  • 如果禁用二进制日志记录(log_bin为OFF),或者如果禁用log_slave_updates,则服务器将属于每个事务的GTID与表中的事务一起存储。 此外,该表可以定期压缩; 此情况仅不适用于复制中的master,因为在主服务器上,必须启用二进制日志记录才能进行复制。

  • 如果启用了二进制日志记录(log_bin为ON),则无论何时轮询二进制日志或关闭服务器,服务器都会将写入先前二进制日志的所有事务的GTID写入mysql.gtid_executed表。 这种情况适用于复制主服务器或启用了二进制日志记录的复制从服务器。

    如果服务器意外停止,则当前二进制日志中的GTID集不会保存在mysql.gtid_executed表中。 在这种情况下,这些GTID会在恢复期间添加到表和gtid_executed系统变量中的GTID集合中。

    启用二进制日志记录时,mysql.gtid_executed表不会为所有已执行的事务提供GTID的完整记录。 该信息由gtid_executed系统变量的全局值提供。

命令RESET MASTER将重置mysql.gtid_executed表。

1.3 mysql.gtid_executed 表压缩

启用GTID时,服务器会定期在mysql.gtid_executed表上执行此类压缩。 通过设置gtid_executed_compression_period系统变量,您可以控制压缩表之前允许的事务数,从而控制压缩率。 该变量的默认值为1000; 这意味着,默认情况下,在每1000次事务之后执行表的压缩。 将gtid_executed_compression_period设置为0可以防止执行压缩; 但是,如果执行此操作,您应该为gtid_executed表可能需要的磁盘空间量的大幅增加做好准备。

【注意】:

启用binlog时,且不使用gtid_executed_compression_period的值,会在每个binlog轮换时压缩mysql.gtid_executed表。

压缩前:

mysql> SELECT * FROM mysql.gtid_executed;
+--------------------------------------+----------------+--------------+
| source_uuid | interval_start | interval_end |
|--------------------------------------+----------------+--------------|
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 37 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 38 | 38 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 39 | 39 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 40 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 41 | 41 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 42 | 42 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 43 | 43 |
...

压缩后:

+--------------------------------------+----------------+--------------+
| source_uuid | interval_start | interval_end |
|--------------------------------------+----------------+--------------|
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 43 |
...

mysql.gtid_executed表的压缩由名为thread/sql/compress_gtid_table的专用前台线程执行。 该线程未在SHOW PROCESSLIST的输出中列出,但可以将其视为performance_schema.threads表中的一行,如下所示:

mysql> SELECT * FROM performance_schema.threads WHERE NAME LIKE '%gtid%'\G
*************************** 1. row ***************************
THREAD_ID: 26
NAME: thread/sql/compress_gtid_table
TYPE: FOREGROUND
PROCESSLIST_ID: 1
PROCESSLIST_USER: NULL
PROCESSLIST_HOST: NULL
PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Daemon
PROCESSLIST_TIME: 1509
PROCESSLIST_STATE: Suspending
PROCESSLIST_INFO: NULL
PARENT_THREAD_ID: 1
ROLE: NULL
INSTRUMENTED: YES
HISTORY: YES
CONNECTION_TYPE: NULL
THREAD_OS_ID: 18677

thread/sql/compress_gtid_table线程通常会休眠,直到执行了gtid_executed_compression_period事务,然后唤醒以执行mysql.gtid_executed表的压缩,如前所述。 然后它休眠直到另一个gtid_executed_compression_period事务发生,然后唤醒再次执行压缩,无限期地重复此循环。 禁用二进制日志记录时将此值设置为0意味着线程始终处于休眠状态且从不唤醒。

2. GTID 生命周期

GTID的生命周期包括以下步骤:

  1. Master产生GTID:在Master端产生一个GTID的信息,并保存到binlog中。
  2. 发送Binlog信息到从库上,将binlog信息发送到SLAVE所在的服务器上。将SLAVE中的gtid_next的值设置为GTID的值。
  3. SLAVE执行GTID。SLAVE首先验证是否在自己的二进制日志中使用了这个GTID号
  4. SLAVE不生成GTID。由于GTID不为空,SLAVE不会尝试为该事务生成新的GTID,而是从gtid_next 中读取GTID值,写入二进制日志中,来标识一个事务的GTID的值。

gtid_purged

gtid_purged系统变量(@@global.gtid_purged)中的GTID集包含已在服务器上提交但在服务器上的任何二进制日志文件中不存在的所有事务的GTID。 以下类别的GTID在此集合中:

  • 在slave上禁用binlog提交的复制事务的GTID
  • 已写入已清除的binlog的事务的GTID
  • 由语句SET @@global.gtid_purged明确添加到集合中的GTID

服务器启动时,将初始化gtid_purged系统变量中的GTID集。 每个二进制日志文件都以事件Previous_gtids_log_event开头,该事件包含所有先前二进制日志文件中的GTID集(由前一个文件的Previous_gtids_log_event中的GTID和文件本身中每个Gtid_log_event的GTID组成)。 最旧的二进制日志文件中的Previous_gtids_log_event的内容用于在服务器启动时初始化gtid_purged集,并在清除二进制日志文件时维护该集。

3. 使用GTID搭建主从

GTID使用master_auto_position=1代替了基于binlog和position号的主从复制搭建方式,更便于主从复制的搭建。

3.1 环境准备

类型 ip prot server-id 是否开启binlog binlog格式 log_slave_updates参数
master 192.168.56.100 3307 1003307 log-bin = /data/mysql/mysql3307/logs/my3307_binlog binlog_format = row log_slave_updates=1
slave 192.168.56.200 3307 2003307 log-bin = /data/mysql/mysql3307/logs/my3307_binlog binlog_format = row log_slave_updates=1

3.2 配置GTID主从的参数

  • server_id: 设置MySQL实例的server_id,每个server_id不能一样
  • gtid_mode=ON: MySQL实例开启GTID模式
  • enforce_gtid_consitency=ON:使用GTID模式复制时,需要开启参数,用来保证数据的一致性。
  • log-bin: MySQL必须要开启binlog
  • log-slave-updates=1:决定SLAVE从Master接收到更新且执行是否记录到SLAVE的binlog中
  • binlog_format=ROW: binlog格式为row
  • skip-slave-start=1(可选): 当SLAVE数据库启动的时候,SLAVE不会启动复制

3.3 在master上操作

1) 创建复制账号

create user 'repl'@'%' identified by 'wanbin';

grant replication slave on *.* to 'repl'@'%';

2) master数据库利用xtrabackup备份至slave上


innobackupex --defaults-file=/etc/my3307.cnf -uroot -pmysql --stream=tar ./ |ssh root@mysqldb2 "cat - > /data/backup/dbback`date +%Y%m%d_%H%M%S`.tar"

3.4 在slave上操作

  1. 解压备份
mkdir dbback20181012_151213

tar -xvf dbback20181012_151213.tar -C dbback20181012_151213
  1. prepare备份
innobackupex --apply-log /data/backup/dbback20181012_151213/
  1. 恢复备份
innobackupex --defaults-file=/etc/my3307.cnf --copy-back /data/backup/dbback20181012_151213/

chown -R mysql:mysql /data/mysql/mysql3307/data

mysqld --defaults-file=/etc/my3307.cnf &
  1. 过滤掉已执行过的gtid
1)查看xtrabackup_info文件中的gtid信息
cat xtrabackup_info|grep binlog_pos
binlog_pos = filename 'my3307_binlog.000002', position '2401', GTID of the last change '3a068bf8-cdeb-11e8-8176-080027b0b461:1-10' 2)查看slave已执行的gtid是否为空,如果不为空,需要执行reset MASTER进行清理,否则无法设置gtid。
root@localhost [(none)] 15:50:41>show master status \G;
*************************** 1. row ***************************
File: my3307_binlog.000002
Position: 234
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 3a068bf8-cdeb-11e8-8176-080027b0b461:1-10,
ffe86a27-cdef-11e8-bb92-0800275b8a9a:1-2
1 row in set (0.00 sec) 3)执行reset master
root@localhost [(none)] 15:50:52>reset master;
Query OK, 0 rows affected (0.04 sec) root@localhost [(none)] 15:53:18>show master status\G
*************************** 1. row ***************************
File: my3307_binlog.000001
Position: 154
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec) 4)执行GTID_PURGED
root@localhost [(none)] 15:56:32>set session sql_log_bin = 0;
root@localhost [(none)] 15:56:53>set global gtid_purged='3a068bf8-cdeb-11e8-8176-080027b0b461:1-10';
root@localhost [(none)] 15:57:58>set session sql_log_bin = 1;
  1. 配置主从
执行help change master to 可以获取change master to命令

CHANGE MASTER TO
MASTER_HOST='192.168.56.100',
MASTER_USER='repl',
MASTER_PASSWORD='wanbin',
MASTER_PORT=3307,
master_auto_position=1;
  1. 开始主从复制
start slave;

#如果想分别指定启动线程,可以使用如下命令
START SLAVE IO_THREAD;
START SLAVE SQL_THREAD; #同样关闭命令:
STOP SLAVE; #分别关闭命令:
STOP SLAVE IO_THREAD;
STOP SLAVE SQL_THREAD;

4. 使用gtid进行复制的限制

由于基于GTID的复制依赖于事务,因此在使用时不支持MySQL中可用的某些功能。本节提供有关使用GTID进行复制的限制和限制的信息。

4.1 非事务性存储引擎的更新

使用GTID时,使用非事务性存储引擎(如MyISAM)对表的更新不能在与使用事务性存储引擎(如InnoDB)的表的更新相同的语句或事务中进行。

此限制是由于对使用非事务性存储引擎的表的更新与对同一事务中使用事务存储引擎的表的更新混合可能导致将多个GTID分配给同一事务。

当master和slave使用不同的存储引擎用于同一个表的相应版本时,也会发生这样的问题,其中一个存储引擎是事务性的而另一个不是。 还要注意,定义为在非事务性表上运行的触发器可能是导致这些问题的原因。

在刚刚提到的任何一种情况下,事务和GTID之间的一对一对应关系被破坏,结果是基于GTID的复制无法正常运行。

4.2 CREATE TABLE … SELECT 语句

CREATE TABLE … SELECT对于基于语句的复制是不安全的。 使用基于行的复制时,此语句实际上记录为两个单独的事件 - 一个用于创建表,另一个用于将源表中的行插入刚刚创建的新表中。 当在事务中执行此语句时,在某些情况下,这两个事件可能会接收相同的事务标识符,这意味着slave将跳过包含插入的事务。 因此,使用基于GTID的复制时不支持CREATE TABLE … SELECT。

4.3 临时表

使用GTID时(即,enforce_gtid_consistency系统变量设置为ON时),事务,过程,函数和触发器内不支持CREATE TEMPORARY TABLE和DROP TEMPORARY TABLE语句。 可以在启用GTID的情况下使用这些语句,但仅限于任何事务之外,并且仅使用autocommit = 1。

4.4 防止执行不受支持的语句

要防止执行会导致基于GTID的复制失败的语句,必须在启用GTID时使用–enforce-gtid-consistency选项启动所有服务器。

4.5 跳过事务

使用GTID时不支持sql_slave_skip_counter。 如果您需要跳过事务,请使用master的gtid_executed变量的值; 有关详细信息,请参考

https://dev.mysql.com/doc/refman/5.7/en/replication-gtids-failover.html#replication-gtids-failover-empty

4.6 Ignoring servers

使用GTID时,不推荐使用CHANGE MASTER TO语句的IGNORE_SERVER_IDS选项,因为已经应用的事务会自动被忽略。 在启动基于GTID的复制之前,请检查并清除之前在相关服务器上设置的所有忽略的服务器ID列表。 可以为各个通道发出的SHOW_SLAVE_STATUS语句显示已忽略的服务器ID列表(如果有)。 如果没有列表,则Replicate_Ignore_Server_Ids字段为空。

4.7 GTID mode and mysqldump

如果目标服务器的二进制日志中没有GTID,则可以将使用mysqldump创建的转储导入到启用了GTID模式的MySQL服务器中。

4.8 GTID mode and mysql_upgrade

当服务器在启用全局事务标识符(GTID)的情况下运行时(gtid_mode = ON),请不要通过mysql_upgrade启用二进制日志记录(–write-binlog选项)。

MySQL 使用GTID进行复制的更多相关文章

  1. MYSQL 基于GTID的复制

    1.概述 从MYSQL5.6 开始,mysql开始支持GTID复制. 基于日志点复制的缺点: 从那个二进制日志的偏移量进行增量同步,如果指定错误会造成遗漏或者重复,导致数据不一致. 基于GTID复制: ...

  2. 解决mysql开启GTID主从同步出现1236错误问题【转】

    最近遇到mysql开启gtid做复制时,从库出现1236错误,导致同步无法进行,本文就这问题记录下处理步骤,有关gtid知识在这里不做介绍,mysql版本为5.7.16. 一.错误原因分析 错误信息如 ...

  3. 解决mysql开启GTID主从同步出现1236错误问题

    解决mysql开启GTID主从同步出现1236错误问题     最近遇到mysql开启gtid做复制时,从库出现1236错误,导致同步无法进行,本文就这问题记录下处理步骤,有关gtid知识在这里不做介 ...

  4. MySQL的GTID复制与传统复制的相互转换

    主库:192.168.225.128:3307从库1:192.168.225.129:3307 Gtid作为5.6版本以来的杀手级特性,却因为不支持拓扑结构内开关而饱受诟病.如果你需要从未开启GTID ...

  5. Mysql基于GTID复制模式-运维小结 (完整篇)

    先来看mysql5.6主从同步操作时遇到的一个报错:mysql> change master to master_host='192.168.10.59',master_user='repli' ...

  6. 详解MySQL主从复制实战 - 基于GTID的复制

    基于GTID的复制 简介 基于GTID的复制是MySQL 5.6后新增的复制方式. GTID (global transaction identifier) 即全局事务ID, 保证了在每个在主库上提交 ...

  7. MySQL的GTID复制

    从mysql5.6开始引入全局事务标识符(GTID),即每个事务都有一个唯一的标识符.服务器上的每个事务都被分配一个唯一的事务标识符,这是一个64位非零的数值,根据事务提交的顺序分配.GTID的构成是 ...

  8. mysql的GTID复制和多源复制

    配置基于GTID的复制--------------------------------------------在参数文件/etc/my.cnf增加下面内容:主库master_info_reposito ...

  9. MySQL的GTID复制与传统复制的相互切换

    MySQL的GTID复制与传统复制的相互转换 1. GTID复制转换成传统复制 1.1 环境准备 1.2 停止slave 1.3 查看当前主从状态 1.4 change master 1.5 启动主从 ...

随机推荐

  1. RobotFramework学习笔记-Web自动化

    一.窗口关键字使用 1.当前浏览器弹出新的窗口 使用Select Window和Close Window处理弹出窗口.实际使用中Select Window不一定会一次选中,通常会结合Wait Unti ...

  2. 管理docker容器

    如果在容器中启动sshd,存在开销和攻击面增大的问题.同时也违反了Docker所倡导的一个容器一个进程的原则. docker attach 37d61466c69e \\注意:如果在stdin中exi ...

  3. POJ-3177-RedundantPaths(边联通分量,缩点)

    链接:https://vjudge.net/problem/POJ-3177#author=Dillydally 题意: 有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可 ...

  4. NET Core使用Quartz

    NET Core使用Quartz 一.前言运用场景 Quartz.Net是一个强大.开源.轻量的作业调度框架,在平时的项目开发当中也会时不时的需要运用到定时调度方面的功能,例如每日凌晨需要统计前一天的 ...

  5. NET Core 事件总线

    NET Core 事件总线,分布式事务解决方案:CAP 背景 相信前面几篇关于微服务的文章也介绍了那么多了,在构建微服务的过程中确实需要这么一个东西,即便不是在构建微服务,那么在构建分布式应用的过程中 ...

  6. postgresql修改数据库名

    alter database abc rename to cba;

  7. java中两个map比较

    一 /** * 用map的keySet()的迭代器(性能效率较低) * */ public void compareMap1 (){ Map<String, String> m1 = ne ...

  8. rabbitmq 不发送ack消息如何处理: RabbitMQ 消息确认以及消息消费方处理消息时候抛出了异常以

    本篇的代码使用的前面两篇文章<RabbitMQ与Spring整合之消息生产方>和<RabbitMQ与Spring整合之消息消费方>的代码,这两篇文件里配置文件的名称不正确,不可 ...

  9. vuejs 组件 移动端push 没有渲染页面

    this.idcards.push(arr) 这个无效 就知道了 vuejs有个跟push相同的方法 console.log(this.list.push.toString()) 这个push是个同名 ...

  10. Garmin APP开发之布局

    上一章节介绍了garmin app开发的入门,包括garmin-sdk,开发工具的安装部署,文章结尾我们新建了我们的第一个app程序Garmin开发-入门: http://tieba.baidu.co ...