MySQL GTID介绍及搭建GTID从库

MySQL GTID介绍及搭建GTID从库

文章来源: 陶老师运维笔记- 微信公众号

1. GTID 介绍

1.1 GTID概念

GTID (Global Transaction ID) 是对于一个已提交事务的编号,并且是一个全局唯一的编号。 GTID 实际上 是由 UUID+TID 组成的。其中UUID 是一个 MySQL 实例的唯一标识,记录在$datadir/auto.cnf。TID 代表了该实例上已经提交的事务数量,并且随着事务提交单调递增
如下是GTID的几个具体形式:

#GTID
3E11FA47-71CA-11E1-9E33-C80AA9429562:23
#一组连续的事务可以用 - 连接的事务序号范围表示
e6954592-8dba-11e6-af0e-fa163e1cf111:1-5
#多个 MySQL 实例的事务,它们之间用逗号分隔。如果来自同一 MySQL 实例的事务序号有多个范围区间,各组范围之间用冒号分隔。
e6954592-8dba-11e6-af0e-fa163e1cf111:1-5:11-18,
e6954592-8dba-11e6-af0e-fa163e1cf3f2:1-27

GTID:

  • GTID 是由 UUID+TID 组成的,UUID标识MySQL实例,TID 代表该实例上已提交的事务数量。
  • GTID 集合可以包含来自多个 MySQL 实例的事务,它们之间用逗号分隔。
  • 一组连续的事务可以用 - 连接的事务序号范围表示。
  • 使用 SHOW MASTER STATUS 实时看当前的事务执行数。

1.2 GTID查看

可以用SHOW MASTER STATUS实时看当前的事务执行数。

show global variables like '%gtid%';
select @@server_uuid;
+--------------------------------------+
| @@server_uuid                        |
+--------------------------------------+
| a01f3a3f-1eee-11ea-b1e4-a0369fac2de4 |
+--------------------------------------+
 show master status;
+------------------+----------+--------------+------------------+-------------------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+------------------+----------+--------------+------------------+-------------------------------------------+
| mysql-bin.000002 |      191 |              |                  | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-14 |
+------------------+----------+--------------+------------------+-------------------------------------------+
show global variables like  'gtid_executed';
+---------------+-------------------------------------------+
| Variable_name | Value                                     |
+---------------+-------------------------------------------+
| gtid_executed | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-14 |
+---------------+-------------------------------------------+

MySQL5.7后还可以通过mysql.gtid_executed表来查看。

SELECT * FROM mysql.gtid_executed;
+--------------------------------------+----------------+--------------+
| source_uuid                          | interval_start | interval_end |
+--------------------------------------+----------------+--------------+
| aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa |              1 |           40 |
| aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa |        1000003 |      1000004 |
| aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa |        2000003 |      2000006 |
+--------------------------------------+----------------+--------------+

1.3 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不会启动复制

配置示例:

#假定port为3106
[mysqld]
datadir=/data1/mysql_3106
socket=/tmp/mysql3106.sock
log-bin=mysql-bin                 # mysql 5.6必须项,mysql 5.7非必须项
sync-binlog=1                     # 建议项
binlog_format=row                 # 建议项
relay-log=relay-bin               # 必须项
server-id=413106                  #必须项,不能和其它实例相同
log-error=mysqld_error.log
pid-file=mysqld.pid

log-slave-updates = 1             #mysql5.6 必须
enforce_gtid_consistency=on       # 必须项
gtid_mode=on                      # 必须项

查看情况:

## 开启GTID
show global variables like '%gtid%';
+---------------------------------+-------------------------------------------+
| Variable_name                   | Value                                     |
+---------------------------------+-------------------------------------------+
| binlog_gtid_simple_recovery     | OFF                                       |
| enforce_gtid_consistency        | ON                                        |
| gtid_executed                   | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-14 |
| gtid_mode                       | ON                                        |
| gtid_owned                      |                                           |
| gtid_purged                     |                                           |
| simplified_binlog_gtid_recovery | OFF                                       |
+---------------------------------+-------------------------------------------+

1.4 GTID作用

主要有以下两点:

  • 根据 GTID 可以知道事务最初是在哪个实例上提交的
  • GTID 的存在方便了 Replication 的 Failover

MySQL 5.6 的 GTID 出现以前 Replication Failover 的操作过程。假设我们有一个如下图的环境:


Failover

此时,Server A 的服务器宕机,需要将业务切换到 Server B 上。同时,我们又需要将 Server C 的复制源改成 Server B。复制源修改的命令语法很简单即:

CHANGE MASTER TO MASTER_HOST='xxx', MASTER_LOG_FILE='xxx', MASTER_LOG_POS=nnnn;

这种方式的难点在于,由于同一个事务在每台机器上所在的 binlog 名字和位置都不一样,那么怎么找到 Server C 当前同步停止点对应 Server B 上 master_log_file 和 master_log_pos 的位置就成为了难题。

MySQL5.6 的 GTID 出现后,就显得非常的简单。
由于同一事务的 GTID 在所有节点上的值一致,那么根据 Server C 当前停止点的 GTID 就能唯一定位到 Server B 上的GTID。甚至由于 MASTER_AUTO_POSITION 功能的出现,我们都不需要知道 GTID 的具体值。直接使用以下命令就可以直接完成 Failover 的工作。

CHANGE MASTER TO MASTER_HOST='xxx', MASTER_AUTO_POSITION=='xxx'

1.5 GTID复制限制

GTID的复制依赖于事务,因此在使用时不支持MySQL中可用的某些功能。

  • 非事务性存储引擎的更新
    同一事务中不能同时更新事务表与非事务表(MyISAM),建议都选择 Innodb 作为默认的数据库引擎。

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

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

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

  • 跳过事务
    使用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

2. GTID工作原理

2.1 GTID 生命周期

master 更新数据时,会在事务前产生 GTID,一同记录到 binlog 日志中
slave 端的 i/o 线程将变更的 binlog,写入到本地的 relay log 中,读取值是根据gitd_next变量,告诉我们slave下一个执行哪个GTID。
sql 线程从 relay log 中获取 GTID,然后对比 slave 端的 binlog 是否有记录。如果有记录,说明该 GTID 的事务已经执行,slave 会忽略。
如果没有记录,slave 就会从 relay log 中执行该 GTID 的事务,并记录到 binlog。
在解析过程中会判断是否有主键,如果没有就用二级索引,如果没有二级索引就用全部扫描。

GTID的生命周期:

  1. 客户端发送DDL/DML给master上,master首先对此事务生成一个唯一的gtid,假如为uuid_xxx:1,然后立即执行该事务中的操作。
    主从复制的情况下,sync-binlog基本上都会设置为1,这表示在每次提交事务时将缓存中的binlog刷盘。所以,在事务提交前,gtid以及事务相关操作的信息都在缓存中,提交后它们才写入到binlog file中,然后才会被dump线程dump出去。
    即只有提交了的事务,gtid和对应的事务操作才会记录到binlog文件中。记录的格式是先记录gtid,紧跟着再记录事务相关的操作。

  2. 当binlog传送到relay log中后,slave上的SQL线程首先读取该gtid,并 设置变量 gtid_next 的值为该gtid,表示下一个要操作的事务是该gtid。gtid_next 是基于会话的,不同会话的gtid_next不同。

  3. 随后slave检测该gtid在自己的binlog中是否存在。如果存在,则放弃此gtid事务;如果不存在,则将此gtid写入到自己的binlog中,然后立刻执行该事务,并在自己的binlog中记录该事务相关的操作。
    注意,slave上replay的时候,gtid不是提交后才写到自己的binlog file的,而是判断gtid不存在后立即写入binlog file。
    通过这种在执行事务前先检查并写gtid到binlog的机制,不仅可以保证当前会话在此之前没有执行过该事务,还能保证没有其他会话读取了该gtid却没有提交。因为如果其他会话读取了该gtid会立即写入到binlog(不管是否已经开始执行事务),所以当前会话总能读取到binlog中的该gtid,于是当前会话就会放弃该事务。总之,一个gtid事务是决不允许多次执行、多个会话并行执行的。

  4. SLAVE不生成GTID。slave在重放relay log中的事务时,不会自己生成gtid,所以所有的slave(无论是何种方式的一主一从或一主多从复制架构)通过重放relay log中事务获取的gtid都来源于master,并永久保存在slave上。

2.2 GTID生成示例

GTID 的生成受 gtid_next 控制。 在主服务器上gtid_next 是默认的 AUTOMATIC,即在每次事务提交时自动生成新的 GTID 。它从当前已执行的 GTID 集合(即 gtid_executed )中,找一个大于 0 的未使用的最小值作为下个事务 GTID 。同时在 Binlog 的实际的更新事务事件前面插入一条 set gtid_next 事件。
这里以一条 insert 语句来看看 GTID 的生成过程:

Master:

#操作前
192.110.103.41 : test > select @@server_uuid;
+--------------------------------------+
| @@server_uuid                        |
+--------------------------------------+
| a01f3a3f-1eee-11ea-b1e4-a0369fac2de4 |
+--------------------------------------+
192.110.103.41 : test > flush logs;
test>show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
...
| mysql-bin.000005 |       191 |
+------------------+-----------+

192.110.103.41 : test > show master status;
+------------------+----------+--------------+------------------+-------------------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+------------------+----------+--------------+------------------+-------------------------------------------+
| mysql-bin.000005 |      191 |              |                  | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-25 |
+------------------+----------+--------------+------------------+-------------------------------------------+
1 row in set (0.00 sec)

## insert 
192.110.103.41 : test > insert into t1 values(2,'li4');
Query OK, 1 row affected (0.00 sec)

192.110.103.41 : test > show master status;
+------------------+----------+--------------+------------------+-------------------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+------------------+----------+--------------+------------------+-------------------------------------------+
| mysql-bin.000005 |      434 |              |                  | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26 |
+------------------+----------+--------------+------------------+-------------------------------------------+
1 row in set (0.00 sec)

192.110.103.41 : test > show binlog events in 'mysql-bin.000005';
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name         | Pos | Event_type     | Server_id | End_log_pos | Info                                                               |
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
| mysql-bin.000005 |   4 | Format_desc    | 103413106 |         120 | Server ver: 5.6.27-log, Binlog ver: 4                              |
| mysql-bin.000005 | 120 | Previous_gtids | 103413106 |         191 | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-25                          |
| mysql-bin.000005 | 191 | Gtid           | 103413106 |         239 | SET @@SESSION.GTID_NEXT= 'a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:26' |
| mysql-bin.000005 | 239 | Query          | 103413106 |         311 | BEGIN                                                              |
| mysql-bin.000005 | 311 | Table_map      | 103413106 |         359 | table_id: 78 (test.t1)                                             |
| mysql-bin.000005 | 359 | Write_rows     | 103413106 |         403 | table_id: 78 flags: STMT_END_F                                     |
| mysql-bin.000005 | 403 | Xid            | 103413106 |         434 | COMMIT /* xid=132 */                                               |
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
7 rows in set (0.01 sec)

SLAVE:
在从库上回放主库的 Binlog 时,先执行 SET @@SESSION.GTID_NEXT语句,然后再执行 insert 语句,确保在主和备上这条 insert 对应于相同的 GTID。

#slave上信息
show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000001 |      5762 |
...
| mysql-bin.000004 |       425 |
+------------------+-----------+
4 rows in set (0.01 sec)
192.110.103.42 : test > show binlog events in 'mysql-bin.000004';
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name         | Pos | Event_type     | Server_id | End_log_pos | Info                                                               |
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
| mysql-bin.000004 |   4 | Format_desc    | 103423106 |         120 | Server ver: 5.6.27-log, Binlog ver: 4                              |
| mysql-bin.000004 | 120 | Previous_gtids | 103423106 |         191 | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-25                          |
| mysql-bin.000004 | 191 | Gtid           | 103413106 |         239 | SET @@SESSION.GTID_NEXT= 'a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:26' |
| mysql-bin.000004 | 239 | Query          | 103413106 |         302 | BEGIN                                                              |
| mysql-bin.000004 | 302 | Table_map      | 103413106 |         350 | table_id: 72 (test.t1)                                             |
| mysql-bin.000004 | 350 | Write_rows     | 103413106 |         394 | table_id: 72 flags: STMT_END_F                                     |
| mysql-bin.000004 | 394 | Xid            | 103413106 |         425 | COMMIT /* xid=124 */                                               |
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
7 rows in set (0.00 sec)

192.110.103.42 : test > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.110.103.41
                  Master_User: repl
                  Master_Port: 3106
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000005
          Read_Master_Log_Pos: 434
               Relay_Log_File: relay-bin.000013
                Relay_Log_Pos: 550
        Relay_Master_Log_File: mysql-bin.000005
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
          Exec_Master_Log_Pos: 434
              Relay_Log_Space: 998
        Seconds_Behind_Master: 0
             Master_Server_Id: 103413106
                  Master_UUID: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4
             Master_Info_File: /data1/mysql_3106/master.info
      Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
           Retrieved_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26
            Executed_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26
                Auto_Position: 1

2.3 常用GTID参数说明

2.3.1 GTID重要的变量

1) gtid_executed
在当前实例上已执行过的 GTID 集合,实际上包含了所有记录到 binlog 中的事务。设置 set sql_log_bin=0 后执行的事务不会生成 binlog 事件,也不会被记录到 gtid_executed 中。执行 RESET MASTER 可以将gtid_executed变量置空。

2)gtid_purged
gtid_purged表示已经purge掉的gtid。只有 gtid_executed 为空时才能手动设置该变量,此时会同时更新 gtid_executed 为和 gtid_purged 相同的值。(gtid_executed 为空意味着要么之前没有启动过基于 GTID 的复制,要么执行过 RESET MASTER。执行 RESET MASTER 时同样也会把 gtid_purged 置空,即始终保持 gtid_purged 是 gtid_executed 的子集。)
在slave上设置该项时,表示稍后启动io线程和SQL线程都跳过这些gtid,slave上设置时应该让此项的gtid集合等于master上 gtid_executed 的值。

binlog 不可能永远驻留在服务上,需要定期进行清理(通过 expire_logs_days 定期清理)日志。
gtid_purged系统变量(@@global.gtid_purged)用于记录本机上已经执行过,但是已经被清除了的 binlog 事务集合。 它是 gtid_executed 的子集。

以下类别的GTID在此gtid_purged集合中:

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

3) gtid_next
会话级变量,指示如何产生下一个GTID。可能的取值如下:

项目 说明
AUTOMATIC 自动生成下一个 GTID,实现上是分配一个当前实例上尚未执行过的序号最小的 GTID。
ANONYMOUS 设置后执行事务不会产生GTID。
显式指定的GTID 可以指定任意形式合法的 GTID 值,但不能是当前 gtid_executed 中的已经包含的 GTID,否则下次执行事务时会报错。

2.3.2 show slave status中gtid说明

192.110.103.42 : test > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.110.103.41
...
              Master_Log_File: mysql-bin.000005
          Exec_Master_Log_Pos: 434
                  Master_UUID: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4
           Retrieved_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26
            Executed_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26
                Auto_Position: 1
  • Retrieved_Gtid_Set:在开启了gtid复制(即gtid_mode=on)时,slave在启动io线程的时候会检查自己的relay log,并从中检索出gtid集合。也就是说,这代表的是slave已经从master中复制了哪些事务过来。检索出来的gtid不会再请求master发送过来。
  • Executed_Gtid_Set:在开启了gtid复制(即gtid_mode=on)时,它表示已经向自己的binlog中写入了哪些gtid集合。注意,这个值是根据一些状态信息计算出来的,并非binlog中能看到的那些。
  • Auto_Position:开启gtid时是否自动获取binlog坐标。1表示开启,这是gtid复制的默认值。

2.3.3 binlog中gtid说明

用slave机器上binlog示例做gtid说明。

#slave机器上binlog示例.
/usr/local/mysql-5.6.27/bin/mysqlbinlog mysql-bin.000003
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
# at 4
#191215 19:18:43 server id 103423106  end_log_pos 120 CRC32 0x8d6b28f2  Start: binlog v 4, server v 5.6.27-log created 191215 19:18:43
BINLOG '
kxb2XQ+BHCoGdAAAAHgAAAAAAAQANS42LjI3LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAXAAEGggAAAAICAgCAAAACgoKGRkAAfIo
a40=
'/*!*/;
# at 120
#191215 19:18:43 server id 103423106  end_log_pos 191 CRC32 0xfaf65b70  Previous-GTIDs
# a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-24   ### 行1 
# at 191
#191215 19:19:08 server id 103413106  end_log_pos 239 CRC32 0xf260d744  GTID [commit=yes] ### 行2
SET @@SESSION.GTID_NEXT= 'a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:25'/*!*/;  ###行3
# at 239
#191215 19:19:08 server id 103413106  end_log_pos 302 CRC32 0xd6f210a9  Query   thread_id=12    exec_time=0     error_code=0
SET TIMESTAMP=1576408748/*!*/;
...
BEGIN
/*!*/;
# at 302
#191215 19:19:08 server id 103413106  end_log_pos 350 CRC32 0x5970d846  Table_map: `test`.`t1` mapped to number 72
# at 350
#191215 19:19:08 server id 103413106  end_log_pos 394 CRC32 0x36e14e8a  Delete_rows: table id 72 flags: STMT_END_F

BINLOG '
rBb2XRNx9SkGMAAAAF4BAAAAAEgAAAAAAAEABHRlc3QAAnQxAAIDDwKWAABG2HBZ
rBb2XSBx9SkGLAAAAIoBAAAAAEgAAAAAAAEAAgAC//wCAAAAA2xpNIpO4TY=
'/*!*/;
# at 394
#191215 19:19:08 server id 103413106  end_log_pos 425 CRC32 0x68da9583  Xid = 113
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; ## 行6
# at 425
#191215 20:20:39 server id 103423106  end_log_pos 472 CRC32 0x1a938cf2  Rotate to mysql-bin.000004  pos: 4
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
  • "注意行1"中Previous-GTIDs代表的gtid集合是曾经的gtid,换句话说是被purge掉的事务。
  • "注意行2"是两个事务的gtid信息。它们写在每个事务的前面。
  • "注意行3"设置了GTID_NEXT的值,表示读取到了该事务后,那么必须要执行的是稍后列出的这个事务。
  • "注意行6"是在所有事务执行结束时设置的,表示自动获取gtid的值。它对复制是隐身的(也就是说不会dump线程不会将它dump出去),该行的结尾也说了,这一行是mysqlbinlog添加的。

3. 搭建GTID复制主从

全新环境搭建GTID主从复制。主要步骤:

  • 安装MySQL: 安装MySQL软件.
  • 配置文件修改: 在my.cnf中配置gtid_mode=ON,log_slave_updates,enforce_gtid_consistency等参数。
  • 启动MySQL: 修改配置后,启动MySQL。
  • 配置主从关系: change master建立GTID主从复制关系。

3.1 安装MySQL

安装MySQL软件,详细步骤见官方参考。

https://dev.mysql.com/doc/refman/5.6/en/binary-installation.html

3.2 修改配置支持GTID

本实验的MySQL ,Master实例ip为192.110.103.41,port为3106。从库为ip为192.110.103.42,port为3106。
1)Master配置示例:

#假定port为3106
[mysqld]
datadir=/data1/mysql_3106
socket=/tmp/mysql3106.sock
log-bin=mysql-bin                 # mysql 5.6必须项,mysql 5.7非必须项
sync-binlog=1                     # 建议项
binlog_format=row                 # 建议项
relay-log=relay-bin               # 必须项
server-id=413106                  #必须项,不能和其它实例相同
log-error=mysqld_error.log
pid-file=mysqld.pid

log-slave-updates = 1             #mysql5.6 必须,mysql5.7非必须
enforce_gtid_consistency=on       # 必须项
gtid_mode=on                      # 必须项

2)Slave配置示例:

#port为3106
[mysqld]
datadir=/data1/mysql_3106
socket=/tmp/mysql3106.sock
log-bin=mysql-bin                 # mysql 5.6必须项,mysql 5.7非必须项
sync-binlog=1                     # 建议项
binlog_format=row                 # 建议项
relay-log=relay-bin               # 必须项
server-id=423106                  # 必须项,不能和其它实例相同
log-error=mysqld_error.log
pid-file=mysqld.pid

log-slave-updates = 1             #mysql5.6 必须,mysql5.7非必须
enforce_gtid_consistency=on       # 必须项
gtid_mode=on                      # 必须项

log-slave-updates = 1 选项会增大了从服务器的IO负载, 在MySQL5.7版本已非必须。

3.3 启动MySQL

su - mysql -c "/usr/local/mysql/bin/mysqld_safe --defaults-file=/data1/mysql_3106/etc/my.cnf &"

3.4 创建复制账号

在master上创建一个用于复制的用户repl。

# master上执行
GRANT SUPER,REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO repl@'192.%.%.%' identified by 'repl';

3.5 建立主从关系

在从库上执行change Master,以建立主从关系,因为master上的binlog没有删除过,所以在slave上直接change master to配置连接参数。
注意: MySQL5.6和MySQL5.7 change语法略有区别。

# MySQL5.6slave上执行
#reset slave all;
reset master;
CHANGE MASTER TO MASTER_HOST='192.110.103.41', MASTER_PORT=3106, MASTER_USER='repl', MASTER_PASSWORD='repl',MASTER_AUTO_POSITION = 1;
start slave;
show slave status\G

## MySQL5.7 slave命令
#MySQL 5.7,没有在change master to语句中加入user和password项,而是在start slave语句中使用,否则会警告。
change master to master_host='192.110.103.41',master_port=3106,master_auto_position=1; 
start slave user='repl' password='repl';
show slave status\G

3.6 检查验证

检查GTID情况

## 开启GTID
#show global variables like '%gtid%';
#Master实例上
192.110.103.41 : (none) >  show master status;
+------------------+----------+--------------+------------------+-------------------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+------------------+----------+--------------+------------------+-------------------------------------------+
| mysql-bin.000002 |     1233 |              |                  | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-19 |
+------------------+----------+--------------+------------------+-------------------------------------------+
1 row in set (0.00 sec)

192.110.103.41 : (none) > show global variables like '%gtid%';
+---------------------------------+-------------------------------------------+
| Variable_name                   | Value                                     |
+---------------------------------+-------------------------------------------+
| binlog_gtid_simple_recovery     | OFF                                       |
| enforce_gtid_consistency        | ON                                        |
| gtid_executed                   | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-19 |
| gtid_mode                       | ON                                        |
| gtid_owned                      |                                           |
| gtid_purged                     |                                           |
| simplified_binlog_gtid_recovery | OFF                                       |
+---------------------------------+-------------------------------------------+

## slave 实例上
root@127.0.0.1 : (none) >  show master status;
+------------------+----------+--------------+------------------+-------------------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+------------------+----------+--------------+------------------+-------------------------------------------+
| mysql-bin.000001 |     5261 |              |                  | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-19 |
+------------------+----------+--------------+------------------+-------------------------------------------+
1 row in set (0.00 sec)

查看主从同步情况:

root@127.0.0.1 : (none) > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.110.103.41
                  Master_User: repl
                  Master_Port: 3106
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000002
          Read_Master_Log_Pos: 1233
               Relay_Log_File: relay-bin.000004
                Relay_Log_Pos: 1443
        Relay_Master_Log_File: mysql-bin.000002
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 1233
              Relay_Log_Space: 1728
              Until_Condition: None
        Seconds_Behind_Master: 0
             Master_Server_Id: 103413106
                  Master_UUID: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4
             Master_Info_File: /data1/mysql_3106/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
           Master_Retry_Count: 86400
...
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-19
            Executed_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-19
                Auto_Position: 1
1 row in set (0.00 sec)

验证数据:

#master
create table if not exists t1 (id int(10) PRIMARY KEY AUTO_INCREMENT,name varchar(50) NOT NULL);      
insert into t1 values(1,'zhang3');
#slave 
select * from t1;
+----+--------+
| id | name   |
+----+--------+
|  1 | zhang3 |
+----+--------+

4. 添加新的slave到gtid复制集群

线上环境,往往会定期删除一部分binlog。咱们模似这种情况下从库制作。

当master没有删除过任何binlog时,可以方便地向复制结构中添加新的slave,因为slave会复制所有的binlog到自己relay log中并replay。即类似上节的命令即可 change master to master_host='xxx',master_port=3xx,master_auto_position=1;

当master删除过一部分binlog后,在向复制结构中添加新的slave时,必须先获取到master binlog中当前已记录的第一个gtid之前的所有数据,然后恢复到slave上。只有slave上具有了这部分基准数据,才能保证和master的数据一致性。

4.1 在master实例上删除binlog

master实例用 purge master logs to 'xxx';来清理binlog。

#master上执行
>flush logs;
>show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000001 |      4266 |
...
| mysql-bin.000005 |       481 |
| mysql-bin.000006 |       191 |
+------------------+-----------+
>purge master logs to 'mysql-bin.000006';
test > show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000006 |       191 |
+------------------+-----------+
1 row in set (0.00 sec)
192.110.103.41 : test > show master status;
+------------------+----------+--------------+------------------+-------------------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+------------------+----------+--------------+------------------+-------------------------------------------+
| mysql-bin.000006 |      191 |              |                  | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26 |
+------------------+----------+--------------+------------------+-------------------------------------------+

4.2 GTID从库搭建步骤

GTID主从库搭建

使用骏马金龙老师的MGR图来说明搭建过程:

来源: 骏马金龙MGR介绍 https://www.cnblogs.com/f-ck-need-u/p/9164823.html

GTID主从库搭建

假如当前master的gtid为A3,已经purge掉的gtid为"1-->A1",备份到slave上的数据为1-A2部分。

如果A1 = 0,表示master的binlog没有被Purge过。slave可以直接开启gtid复制,但这样可能速度较慢,因为slave要复制所有binlog。也可以将master数据备份到slave上,然后设置 gtid_purged 跳过备份结束时的gtid,这样速度较快。

如果A1 != 0,表示master上的binlog中删除了一部分gtid。此时slave上必须先从master处恢复purge掉的那部分日志对应的数据。上图中备份结束时的GTID为A2。然后slave开启复制,唯一需要考虑的是"是否需要设置 gtid_purged 跳过一部分gtid以避免重复执行"。

备份数据到slave上,方式可以是mysqldump、冷备份、xtrabackup备份都行。由于gtid复制的特性,所需要的操作都很少,也很简单,前提是理解了"gtid的生命周期"。

4.3 用mysqldump搭建GTID从库

1)安装MySQL

在需要制作从库的机器上安装MySQL并开启GTID。

2)开启GTID

开启GTID配置,并启动MySQL服务。

3)制做全量备份

在主库上用mysqldump做一个全量备份,这里使用 --all-databases选项是因为基于 GTID 的复制会记录全部的事务, 所以要构建一个完整的dump。

#在master上执行
/usr/local/mysql-5.6.27/bin/mysqldump  --all-databases --default-character-set=utf8  --triggers -R -q --events  --master-data=2 --single-transaction --host=127.0.0.1 --port=3106 --user=root -pxx  > my3106.sql

4)传输备份文件

#slave机器
nc -l 1234 | tar xzvf - >my3106.sql
#master机器发送
tar czvf - my3106.sql| nc 192.110.103.43 1234

5)新从库reset master

备份文件信息: 备份文件里包含了设置 gtid_purged 的语句,需要reset master,否则会出错

SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
SET @@SESSION.SQL_LOG_BIN= 0;
-- GTID state at the beginning of the backup 
--

SET @@GLOBAL.GTID_PURGED='a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26'; #这儿可能出错.
--
-- Position to start replication or point-in-time recovery from
--

-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000006', MASTER_LOG_POS=191;
--
-- Current Database: `mysql`
--

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysql` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `mysql`;
--
-- Table structure for table `columns_priv`
DROP TABLE IF EXISTS `columns_priv`;...

在从库恢复数据前需要先通过 reset master 清空 gtid_executed 变量

#从库上reset master
root@127.0.0.1 : (none) > show master status;
+------------------+----------+--------------+------------------+-------------------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+------------------+----------+--------------+------------------+-------------------------------------------+
| mysql-bin.000002 |      191 |              |                  | 49a0c8e8-1f45-11ea-b419-a0369fa6cd30:1-14 |
+------------------+----------+--------------+------------------+-------------------------------------------+
1 row in set (0.00 sec)

root@127.0.0.1 : (none) > show global variables like '%gtid%';
+---------------------------------+-------------------------------------------+
| Variable_name                   | Value                                     |
+---------------------------------+-------------------------------------------+
| binlog_gtid_simple_recovery     | OFF                                       |
| enforce_gtid_consistency        | ON                                        |
| gtid_executed                   | 49a0c8e8-1f45-11ea-b419-a0369fa6cd30:1-14 |
| gtid_mode                       | ON                                        |
| gtid_owned                      |                                           |
| gtid_purged                     |                                           |
| simplified_binlog_gtid_recovery | OFF                                       |
+---------------------------------+-------------------------------------------+
7 rows in set (0.01 sec)

root@127.0.0.1 : (none) > reset master;
Query OK, 0 rows affected (0.00 sec)

root@127.0.0.1 : (none) > show global variables like '%gtid%';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| binlog_gtid_simple_recovery     | OFF   |
| enforce_gtid_consistency        | ON    |
| gtid_executed                   |       |
| gtid_mode                       | ON    |
| gtid_owned                      |       |
| gtid_purged                     |       |
| simplified_binlog_gtid_recovery | OFF   |
+---------------------------------+-------+
7 rows in set (0.00 sec)

root@127.0.0.1 : (none) > show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |      151 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

说明:
若没有reset master,执行设置 GTID_PURGED 的 SQL 时会出错误GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty

 /usr/local/mysql/bin/mysql -h 127.0.0.1 -P 3106 -uroot -p < my3106.sql 
Enter password: 
ERROR 1840 (HY000) at line 24: @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.

如果不希望备份文件中生成设置 GTID_PURGED 的 SQL,可以给 mysqldump传入 --set-gtid-purged=OFF 关闭。

6)新从库导入备份数据

#导入数据
/usr/local/mysql/bin/mysql -h 127.0.0.1 -P 3106 -uroot -p < my3106.sql 
Enter password: 

导入数据后slave状态

导入备份数据后,可注意到gtid_executed, gtid_purged的值都已更改。

root@127.0.0.1 : (none) > show global variables like '%gtid%';
+---------------------------------+-------------------------------------------+
| Variable_name                   | Value                                     |
+---------------------------------+-------------------------------------------+
| binlog_gtid_simple_recovery     | OFF                                       |
| enforce_gtid_consistency        | ON                                        |
| gtid_executed                   | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26 |
| gtid_mode                       | ON                                        |
| gtid_owned                      |                                           |
| gtid_purged                     | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26 |
| simplified_binlog_gtid_recovery | OFF                                       |
+---------------------------------+-------------------------------------------+
7 rows in set (0.00 sec)

root@127.0.0.1 : (none) > show master status;
+------------------+----------+--------------+------------------+-------------------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+------------------+----------+--------------+------------------+-------------------------------------------+
| mysql-bin.000002 |      191 |              |                  | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26 |
+------------------+----------+--------------+------------------+-------------------------------------------+

7)change master
由于恢复出 MySQL 实例已经被设置了正确的 GTID_EXECUTED ,下面以 master_auto_postion = 1 的方式 CHANGE MASTER 到原来的主节点即可开始复制。

CHANGE MASTER TO MASTER_HOST='192.110.103.41', MASTER_PORT=3106, MASTER_USER='repl', MASTER_PASSWORD='repl',MASTER_AUTO_POSITION = 1;
Query OK, 0 rows affected, 2 warnings (0.01 sec)

root@127.0.0.1 : (none) > show slave status\G
            Executed_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26
                Auto_Position: 1
1 row in set (0.00 sec)

root@127.0.0.1 : (none) > start slave;
Query OK, 0 rows affected (0.00 sec)
root@127.0.0.1 : (none) > show slave status\G
           Retrieved_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:27
            Executed_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-27
                Auto_Position: 1

4.5 用innobackup搭建GTID从库

1)备份master

#授权
#grant all on *.* to backup@'127.0.0.1' identified by 'backup';
#备份
backup_path=/data1/mysqlbackup/`date +%Y%m%d`/mysql_3106
mkdir -p $backup_path
innobackupex --defaults-file=/data1/mysql_3106/my3106.cnf -H 127.0.0.1 -P 3106 --user=backup --password=backup --parallel=4 --throttle=4 --no-timestamp --slave-info   $backup_path/full_backup-`date +'%Y%m%d'` >> $backup_path/mybackup.log 2>&1 
#应用
innobackupex --apply-log $backup_path/full_backup-`date +'%Y%m%d'`
cp /data1/mysql_3106/my3106.cnf $backup_path/full_backup-`date +'%Y%m%d'`

2)传输备份数据

#scp -pr $backup_path/full_backup-`date +'%Y%m%d'` 192.110.103.43:data1/
或用nc传输
#slave接收方
nc -l 1234 | tar xzvf - >full_backup-20191216
#传输方
cd $backup_path/
tar czvf - full_backup-`date +'%Y%m%d'` | nc 192.110.103.43 1234

3)将备份恢复到slave

innobackupex [--defaults-file=MY.CNF] --copy-back  [--defaults-group=GROUP-NAME] BACKUP-DIR
innobackupex --defaults-file=/data1/full_backup-`date +'%Y%m%d'`/my3106.cnf --copy-back  /data1/full_backup-`date +'%Y%m%d'`
#
chown -R mysql:mysql mysql_3106

4)启动MySQL

vim /data1/mysql_3106/my3106.cnf  #修改server_id等。
#
su - mysql -c "/usr/local/mysql/bin/mysqld_safe --defaults-file=/data1/mysql_3106/my3106.cnf &"

启动时状态

192.110.103.43 : (none) > show global variables like '%gtid%';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| binlog_gtid_simple_recovery     | OFF   |
| enforce_gtid_consistency        | ON    |
| gtid_executed                   |       |
| gtid_mode                       | ON    |
| gtid_owned                      |       |
| gtid_purged                     |       |
| simplified_binlog_gtid_recovery | OFF   |
+---------------------------------+-------+
7 rows in set (0.00 sec)

192.110.103.43 : (none) > show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |      151 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

192.110.103.43 : (none) > show slave status\G
Empty set (0.00 sec)

5)查看主的GTID位置

xtrabackup备份数据集却不备份binlog,所以必须先获取此次备份结束时的最后一个事务ID,并在slave上明确指定跳过这些事务,否则slave会再次从master上复制这些binlog并执行,导致数据重复执行。
可以从数据目录中的xtrabackup_info文件中获取。如果不是xtrabackup备份的,那么可以直接从master的show global variables like "gtid_executed";中获取,它表示master中已执行过的事务。

查看xtrabackup_info:

cat xtrabackup_binlog_info 
mysql-bin.000006        964     a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-30
#cat xtrabackup_info
uuid = e5ea7e25-1fad-11ea-b1e4-a0369fac2de4
tool_command = --defaults-file=/data1/mysql_3106/my3106.cnf -H 127.0.0.1 -P 3106 --user=backup --password=... --parallel=4 --throttle=4 --no-timestamp /data1/mysqlbackup/20191216/mysql_3106/full_backup-20191216
ibbackup_version = 2.4.8
server_version = 5.6.27-log
binlog_pos = filename 'mysql-bin.000006', position '964', GTID of the last change 'a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-30'

其中binlog_pos中的GTID对应的就是已备份的数据对应的事务。换句话说,这里的gtid集合1-30表示这30个事务不需要进行复制。
或者在master上直接查看executed的值,注意不是gtid_purged的值,master上的gtid_purged表示的是曾经删除掉的binlog。

#master执行
192.110.103.41 : (none) > show global variables like '%gtid%';
+---------------------------------+-------------------------------------------+
| Variable_name                   | Value                                     |
+---------------------------------+-------------------------------------------+
| binlog_gtid_simple_recovery     | OFF                                       |
| enforce_gtid_consistency        | ON                                        |
| gtid_executed                   | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-30 |
| gtid_mode                       | ON                                        |
| gtid_owned                      |                                           |
| gtid_purged                     | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-26 |
| simplified_binlog_gtid_recovery | OFF                                       |
+---------------------------------+-------------------------------------------+
7 rows in set (0.00 sec)

6)change master开启复制功能

可以在启动slave线程之前使用gtid_purged变量来指定需要跳过的gtid集合。但因为要设置gtid_purged必须保证全局变量gtid_executed为空,所以先在slave上执行reset master(注意,不是reset slave),再设置gtid_purged。

#在新的 slave上执行
#stop slave;

reset slave all;
reset master ;
show master logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000001 |       151 |
+------------------+-----------+

set @@global.gtid_purged='a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-30';

CHANGE MASTER TO MASTER_HOST='192.110.103.41', MASTER_PORT=3106, MASTER_USER='repl', MASTER_PASSWORD='repl',MASTER_AUTO_POSITION = 1;

start slave;

192.110.103.43 : (none) > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.110.103.41
                  Master_User: repl
                  Master_Port: 3106
              Master_Log_File: mysql-bin.000006
          Read_Master_Log_Pos: 964
               Relay_Log_File: relay-bin.000002
                Relay_Log_Pos: 408
        Relay_Master_Log_File: mysql-bin.000006
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 964
        Seconds_Behind_Master: 0
             Master_Server_Id: 103413106
                  Master_UUID: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4
            Executed_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-30
                Auto_Position: 1
1 row in set (0.00 sec)
#
192.110.103.43 : (none) > show master status;
+------------------+----------+--------------+------------------+-------------------------------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+------------------+----------+--------------+------------------+-------------------------------------------+
| mysql-bin.000002 |      191 |              |                  | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-30 |
+------------------+----------+--------------+------------------+-------------------------------------------+
1 row in set (0.00 sec)

查看binlog文件内容

/usr/local/mysql/bin/mysqlbinlog -v --base64-output=decode-rows mysql-bin.000002

##
DELIMITER /*!*/;
# at 4
#191216 11:53:44 server id 103433106  end_log_pos 120 CRC32 0xcf599810  Start: binlog v 4, server v 5.6.27-log created 191216 11:53:44
# Warning: this binlog is either in use or was not closed properly.
# at 120
#191216 11:53:44 server id 103433106  end_log_pos 191 CRC32 0xccbdbaae  Previous-GTIDs
# a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-30
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;

5. GTID常见问题处理

5.1 跳过GTID复制错误

在基于 GTID 的复制拓扑中,要想修复从库的 SQL 线程错误,过去的 SQL_SLAVE_SKIP_COUNTER 方式不再适用。需要通过设置 gtid_next 或 gtid_purged 来完成,当然前提是已经确保主从数据一致,仅仅需要跳过复制错误让复制继续下去。

【现象】

192.110.103.43 : test > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.110.103.41
                  Master_User: repl
                  Master_Port: 3106
              Master_Log_File: mysql-bin.000007
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
                   Last_Errno: 1062
                   Last_Error: Could not execute Write_rows event on table test.tt; Duplicate entry '4' for key 'PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000007, end_log_pos 891
                 Skip_Counter: 0
        Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
               Last_SQL_Errno: 1062
               Last_SQL_Error: Could not execute Write_rows event on table test.tt; Duplicate entry '4' for key 'PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000007, end_log_pos 891

           Retrieved_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:31-35
            Executed_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-34,
bb35a83e-1fb2-11ea-b6e3-a0369fa6cd30:1
                Auto_Position: 1

【原因】

从库和主库数据不一致,从库存在Duplicate entry ,导致同步异常。

【处理】

以前非GTID可以如下,

#普通复制
stop slave;
set global sql_slave_skip_counter = 1;
start slave;

但是GTID复制执行sql_slave_skip_counter将会出错。

#若是GTID复制时将会出错
set global sql_slave_skip_counter = 1;
ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction

GTID复处理方法如下:

#master
flush logs;
192.110.103.41 : test > show master status;
show global variables like '%gtid%';
show binlog events in 'mysql-bin.000007';

#slave
由于在这个GTID必须是连续的,正常情况同一个服务器产生的GTID是不会存在空缺的。所以不能简单的skip掉一个事务,只能通过注入空事物的方法替换掉一个实际操作事务。

192.110.103.43 : test >show slave status\G
当前Executed_Gtid_Set的值a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-34
           Retrieved_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:31-35
            Executed_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-34,
bb35a83e-1fb2-11ea-b6e3-a0369fa6cd30:1

#处理:
stop slave;
set gtid_next='a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:35'; --(last_executed_slave_gtid_on_master + 1)
begin;commit;
set gtid_next='AUTOMATIC';
start slave;
show slave status\G;

设置gtid_next的方法一次只能跳过一个事务,要批量的跳过事务可以通过设置gtid_purged完成。

5.2 从库同步error 1236 ,master has purged binary logs

【现象】

192.110.103.43 : test > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: 
                  Master_Host: 192.110.103.41
                  Master_User: repl
                  Master_Port: 3106
                Connect_Retry: 60
              Master_Log_File: 
               Relay_Log_File: relay-bin.000001
                Relay_Log_Pos: 4
             Slave_IO_Running: No
            Slave_SQL_Running: Yes
                 Skip_Counter: 0
        Seconds_Behind_Master: 0
                Last_IO_Errno: 1236
                Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.'
                  Master_UUID: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4
      Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:35-36
                Auto_Position: 1

【原因】
该错误常是发现在从库的io进程从主库拉取日志时,主库上的binlog被删除了,导致从库获取不到对应的binglog file。

【解决】
方法1: 使用mysqldump或xtrabackup重新搭建GTID从库。
方法2:
在master执行,查看被purge的GTID:

在主上执行,查看被purge的GTID:
192.110.103.41 : test > flush logs;
192.110.103.41 : test > purge master logs to 'mysql-bin.000008'; 
Query OK, 0 rows affected (0.00 sec)

192.110.103.41 : test > show global variables like 'gtid_purged';
+---------------+-------------------------------------------+
| Variable_name | Value                                     |
+---------------+-------------------------------------------+
| gtid_purged   | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-36 |
+---------------+-------------------------------------------+

在从上执行,跳过这个GTID:

192.110.103.43 : test > stop slave;
Query OK, 0 rows affected (0.00 sec)

192.110.103.43 : test > set global gtid_purged = 'a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-36';
ERROR 1840 (HY000): @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.
192.110.103.43 : test > reset master;
Query OK, 0 rows affected (0.00 sec)

192.110.103.43 : test > set global gtid_purged = 'a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-36';
Query OK, 0 rows affected (0.00 sec)

192.110.103.43 : test > show global variables like '%gtid%';
+---------------------------------+-------------------------------------------+
| Variable_name                   | Value                                     |
+---------------------------------+-------------------------------------------+
| binlog_gtid_simple_recovery     | OFF                                       |
| enforce_gtid_consistency        | ON                                        |
| gtid_executed                   | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-36 |
| gtid_mode                       | ON                                        |
| gtid_owned                      |                                           |
| gtid_purged                     | a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-36 |
| simplified_binlog_gtid_recovery | OFF                                       |
+---------------------------------+-------------------------------------------+
7 rows in set (0.00 sec)

192.110.103.43 : test > start slave;
Query OK, 0 rows affected (0.00 sec)
#发现同步正常
192.110.103.43 : test > show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.110.103.41
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
        Seconds_Behind_Master: 0
      Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
           Master_Retry_Count: 86400
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: a01f3a3f-1eee-11ea-b1e4-a0369fac2de4:1-36
                Auto_Position: 1

5.3 主库故障进行切换,恢复服务

【现象】
有一MySQL集群,Server1为主,Server2,Server3为从库。某天Server1故障,需要恢复服务。

【处理】

1)选择新的主库

主有故障时:(切换步骤,需要把其中的一个从设置为主,另一个设置为其的从库)
1:检查从库上,完成事务的从,谁最接近主库
  
server2:   show salve status\G;
  
          Master_Log_File: mysql-bin3306.000002    ---(越大越接近)
          Read_Master_Log_Pos: 4156773
          Exec_Master_Log_Pos: 4156773
  
server3:  show salve status\G;
  
          Master_Log_File: mysql-bin3306.000001   ---(越大越接近)
          Read_Master_Log_Pos: 83795320
          Exec_Master_Log_Pos: 83795320
  
相比之下server2完成的事务要比server3更接近或则等于server1,现在需要把server3设置为server2的从库。

2)切换

从库server3,上重新指定主库信息(指向server2)
   mysql> stop slave;   #千万不要执行 reset master,否则会从最先的GTID上开始执行。
   change master to master_host='192.x.x.41',master_user='rep',master_password='rep',master_port=3306,master_auto_position=1; #指定到server2从上
   start slave;  #成功的切换到新主

3)善后

修改域名及通知业务方观察服务。若是未来server1主库恢复也可切换到server2(完成的事务最接近主库)做为server2从库。

参考:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容