要谈GTID,首先先来了解一下MySQL主从复制。
为什么需要 Mysql 主从复制
谈起为什么在大多数情况下部署 Mysql 常常使用 Mysql 主从模式进行部署这个问题,其中原因主要有以下几点:
- 做数据的热备,主库宕机后备库能够及时替换主库,保证业务可用性,能一定程度避免数据丢失。
- 实现读写分离,主库写,从库读,减小主库的读写压力。当主库执行写过程加锁时,不会堵塞从库读操作,提高了数据的查询效率。
- 应对业务量越来越大,I/O 访问频率过高,单机无法满足的问题。增加多个从库做负载,能够降低整体 I/O 访问频率,提高单个机器 I/O 性能。
数据库常用的主从复制方式
基于 Binlog 复制模式
MySQL 主从复制默认是异步的模式。MySQL增删改操作会全部记录在 Binlog 中,当 slave 节点连接 master 时,会主动从 master 处获取最新的 Binlog 文件。并把 Binlog 存储到本地的 relay log 中,然后去执行 relay log 的更新内容。通过位点同步的原理如下:
1、主库会生成多个 binlog 日志文件。
2、从库的I/O 线程请求指定文件和指定位置的 binlog 日志文件(位点)。
3、主库 dump 线程获取指定位点的 binlog 日志。
4、主库按照从库发送给来的位点信息读取 binlog,然后推送 binlog 给从库。
5、从库将得到的 binlog 写到本地的 relay log (中继日志) 文件中。
6、从库的 SQL 线程读取和解析 relay log 文件。
7、从库的 SQL 线程重放 relay log 中的命令。
当我们使用位点同步的方式时,两种场景下的操作步骤比较复杂。
存在痛点
痛点1:首次开启主从复制的步骤复杂
- 第一次开启主从同步时,要求从库和主库是一致的。
- 找到主库的 binlog 位点。
- 设置从库的 binlog 位点。
- 开启从库的复制线程。
痛点2:恢复主从复制的步骤复杂
- 找到从库复制线程停止时的位点。
- 解决复制异常的事务。无法解决时就需要手动跳过指定类型的错误,比如通过设置slave_skip_errors=1032,1062。当然这个前提条件是跳过这类错误是无损的。(1062 错误是插入数据时唯一键冲突;1032 错误是删除数据时找不到行)
GTID 复制模式
为了解决基于 Binlog 复制模式的痛点,于是在 MySQL 5.6 里面,提供了新的数据恢复思路,这就是基于GTID的复制模式。MySQL能够通过内部机制 GTID 自动找点同步,并且通过 GTID可以保证每个主库提交的事务在集群中都有唯一的一个事务ID,强化了数据库主从的一致性和故障恢复数据的容错能力,在主库宕机发生主从切换的情况下,GTID方式可以让其他从库自动找到新主库复制的位置。而且GTID可以忽略已经执行过的事务,减少了数据发生错误的概率。GTID 同步的原理如下:
- 主节点执行事务提交前会产生一个
GTID
,其会随着事务一起记录到binlog
日志中。 - 从节点
I/O Thread
会读取主节点的binlog
日志文件并存储在从节点的relaylog
日志中。从节点将主节点的GTID
这个值配置到gtid_next
中,即下一个要读取的 GTID 值。 - 从节点SQL线程从relay log中读取
gtid_next
中的值,然后查找自己的binlog
日志中是否有这个GTID
。 - 如果有这个记录,说明这个
GTID
的事务已经执行过了,就忽略掉。 - 如果没有这个记录,从节点会从relay log中执行该GTID的事务,并记录到自己的
binlog
日志中。同时在读取执行事务前会先检查其他session
中是否持有该GTID
,确保不被重复执行。
GTID相关概念介绍
什么是GTID
GTID
指的是全局事务 ID,全称是 Global Transaction Identifier
,具有如下特点:
- GTID事务是全局唯一性的,并且一个事务对应一个GTID值。
- 一个GTID值在同一个MySQL实例上只会执行一次。
GTID 由 server_uuid
+ tid
组成,其中:
- server_uuid:
server_uuid
是在 Mysql 首次启动过程中自动生成的一个uuid(128位)
随机值,生成后会将该值存储到数据目录的auto.cnf
文件中。 - tid: 代表了该实例上已经提交的事务数量,并且随着事务提交单调递增,所以GTID能够保证每个MySQL实例事务的执行(不会重复执行同一个事务,并且会补全没有执行的事务)。
其组成样式如下:
26630fed-fe68-11ec-b051-000c29509871:1-27
GTID相较与传统复制的优势
- 主从搭建更加简便,不用手动特地指定position位置。
- 复制集群内有一个统一的标识,识别、管理上更方便。
- 故障转移failover更容易,不用像传统复制那样需要找 log_File 和 log_Position的位置。
- 通常情况下GTID是连续没有空洞的,更能保证数据的一致性。
- 相对于ROW复制模式,数据安全性更高,切换更简单。
- 比传统的复制更加安全,一个GTID在一个MySQL实例上只会执行一次,避免重复执行导致数据混乱或者主从不一致。
GTID 使用中的限制条件
GTID 复制是针对事务来说的,一个事务只对应一个 GTID,好多的限制就在于此。其中主要限制如下:
- 在一个复制组中,必须要求统一开启GTID或者是关闭GTID。
- 开启GTID需要重启(5.7除外)
- 不支持
create table table_name select * from table_name
语句复制。
原理:
会生成两个sql,一个是DDL创建表SQL,一个是insert into 插入数据的sql。由于DDL会导致自动提交,所以这个sql至少需要两个GTID,但是GTID模式下,只能给这个sql生成一个GTID )
- 不允许在一个事务中既包含事务表(使用
InnoDB
存储引擎的表)的操作又包含非事务表(使用MyISAM
存储引擎的表)。 - 不支持创建或删除临时表操作,如
CREATE TEMPORARY TABLE or DROP TEMPORARY TABLE
语句操作。 - 使用 GTID 复制从库跳过错误时,不支持执行该
sql_slave_skip_counter
参数的语法,传统复制可以使用这个命令跳过事务。
如何开启 GTID 模式
主要在 Mysql 配置文件中添加下面2条配置:
#开启 gtid 模式
gtid_mode=on
#配置不允许任何事务违反 GTID 一致性,用于保证数据一致性
enforce_gtid_consistency=on
从库配置同步的参数:
CHANGE MASTER TO
MASTER_HOST=$host_name
MASTER_PORT=$port
MASTER_USER=$user_name
MASTER_PASSWORD=$password
master_auto_position=1
其中 master_auto_position 标识主从关系使用的 GTID 协议。相比之前 Binlog 复制模式由人为指定binlog的pos位点改为了MASTER_AUTO_POSITION=1
自动获取binlog的pos位点的配置,MASTER_LOG_FILE 和 MASTER_LOG_POS 参数已经不需要了。
查看GTID相关参数
mysql> show variables like '%gtid%';
+----------------------------------+-------------------------------------------+
| Variable_name | Value |
+----------------------------------+-------------------------------------------+
| binlog_gtid_simple_recovery | ON |
| enforce_gtid_consistency | ON |
| gtid_executed_compression_period | 1000 |
| gtid_mode | ON |
| gtid_next | AUTOMATIC |
| gtid_owned | |
| gtid_purged | 26630fed-fe68-11ec-b051-000c29509871:1-26 |
| session_track_gtids | OFF |
+----------------------------------+-------------------------------------------+
8 rows in set (0.00 sec)
- 参数简要说明
参数名 | 含义介绍 |
---|---|
binlog_gtid_simple_recovery | 该参数是控制当MySQL服务重启或启动时候自动寻找GTIDs的值。 |
enforce_gtid_consistency | 该参数是强制要求只允许复制事务安全的事务,请看上面步骤的具体限制条件。 |
gtid_executed_compression_period | 启用GTID时,服务器会定期在mysql.gtid_executed表上执行压缩。通过设置gtid_executed_compression_period系统变量,可以控制压缩表之前允许的事务数,从而控制压缩率。设置为0时,则不进行压缩。 |
gtid_mode | 是否开启GTID模式。 |
gtid_next | 表示下一个要执行的GTID信息。 |
gtid_owned | 该参数包含全局和session,全局表示所有服务器拥有GTIDs,session级别表示当前client拥有的所有GTIDs。 |
gtid_purged | 已经purge掉的GTIDs,purged掉的GTIDs会包含到gtid_executed中。 |
session_track_gtids | 该参数是控制用于捕获的GTIDs和在OK PACKE返回的跟踪器。 |
如何判断复制方式是GTID还是pos
Show slave status 查看Auto_Position字段。0是pos 方式, 1是gtid方式。
mysql> show slave statusG;
Auto_Position: 1
gtid变更为pos方式:
change master to master_auto_position=0;
当前执行gtid信息
mysql> SELECT @@GLOBAL.GTID_EXECUTED;
+-------------------------------------------------------------------------------------+
| @@GLOBAL.GTID_EXECUTED |
+-------------------------------------------------------------------------------------+
| 26630fed-fe68-11ec-b051-000c29509871:1-31,
bf480f97-fe64-11ec-9b9d-000c29af7696:1-3 |
+-------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT * FROM mysql.gtid_executed;
+--------------------------------------+----------------+--------------+
| source_uuid | interval_start | interval_end |
+--------------------------------------+----------------+--------------+
| 26630fed-fe68-11ec-b051-000c29509871 | 1 | 29 |
| 26630fed-fe68-11ec-b051-000c29509871 | 30 | 30 |
| bf480f97-fe64-11ec-9b9d-000c29af7696 | 1 | 1 |
+--------------------------------------+----------------+--------------+
mysql.gtid_executed表是由MySQL服务器提供给内部使用的。它允许副本在副本上禁用二进制日志记录时使用GTIDs,并允许在二进制日志丢失时保留GTID状态。RESET MASTER命令,gtid_executed表将被清除。
服务意外停止的情况下,当前二进制日志文件中的gtid集不会保存在gtid_executed表。在恢复期间,这些gtid将从二进制日志文件添加到表中,以便可以继续复制。
关于gtid_executed表
若MySQL服务器启用了二进制日志,则表mysql.gtid_executed的更新仅在二进制rotation时发生,因为发生重启等情况依旧可以通过扫描二进制日志判断得知当前运行的GTID位置。
简单来说,该表会记录当前执行的GTID在MySQL 5.6中必须配置参数log_slave_updates的最重要原因在于当slave重启后,无法得知当前slave已经运行到的GTID位置,因为变量gtid_executed是一个内存值:MySQL 5.7将gtid_executed这个值给持久化。采用的技巧与MySQL 5.6处理SQL thread保存位置的方式一样,即将GTID值持久化保存在一张InnoDB表中,并与用户事务一起进行提交,从而实现数据的一致性。触发条件:
- 在binlog发生rotate(flush binary logs/达到max_binlog_size)或者关闭服务时,会把所有写入到binlog中的Gtid信息写入到mysql.gtid_executed表。
- 从库:如果没有开启log_bin或者没有开启log_slave_updates,从库在应用relay-log中的每个事务会执行一个insert mysql.gtid_executed操作。
GTID同步状态简单解析
除了传统的查看binlog和pos值之外,GTID模式可以更直观的查看某个事务执行的情况。
mysql> show slave statusG;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.77.135
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master-binlog.000043
Read_Master_Log_Pos: 666
Relay_Log_File: gtidb-relay-bin.000002
Relay_Log_Pos: 585
Relay_Master_Log_File: master-binlog.000043
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 666
Relay_Log_Space: 792
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 100
Master_UUID: 26630fed-fe68-11ec-b051-000c29509871
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set: 26630fed-fe68-11ec-b051-000c29509871:32
Executed_Gtid_Set: 26630fed-fe68-11ec-b051-000c29509871:1-32,
bf480f97-fe64-11ec-9b9d-000c29af7696:1-3
Auto_Position: 1
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
- GTID相关键参数说明
参数名 | 含义介绍 |
---|---|
Retrieved_Gtid_Set | Slave节点已经接收到的Master节点的GTIDs |
Executed_Gtid_Set | Slave节点已经执行的GTIDs |
Auto_Position | 自动获取position位置,显示为1 |
gtid跳过gtid_next
stop slave;
set gtid_next='26630fed-fe68-11ec-b051-000c29509871:37'
begin;commit;
set gtid_next='automatic';
start slave;
备注:该操作类似于sql_slave_skip_counter,只是跳过错误,不能保证数据一致性,需要人工介入,固强烈建议从机开启read_only=1