• mysql主从GTID不一致问题gtid_executed,gtid_purged ,Retrieved_Gtid_Set,Executed_Gtid_Set(I)


    https://www.jb51.net/article/242473.htm

    centos7下mysql双主+keepalived - benjamin杨 - 博客园

    请设置从库为只读read_only=1,super_read_only=1就会大概率避免下面的情况

    1 概念

    1.1

    gtid_executed等价Executed_Gtid_Set参数,已经执行的gtid集合(gtid-sets)。

    gtid_purged,已经清除的gtid集合。

    Retrieved_Gtid_Set:从库已经接收到主库的事务编号(从库的IO线程已经接受到了)

    Executed_Gtid_Set:已经执行的事务编号(从库的执行sql线程已经执行了的sql)

    master: 

    1. mysql> show variables like '%uuid%';
    2. +---------------+--------------------------------------+
    3. | Variable_name | Value |
    4. +---------------+--------------------------------------+
    5. | server_uuid | 21395c77-16b5-11ed-b628-005056b93c30 |
    6. +---------------+--------------------------------------+
    7. 1 row in set (0.01 sec)

    slave:

    1. mysql> show variables like '%uuid%';
    2. +---------------+--------------------------------------+
    3. | Variable_name | Value |
    4. +---------------+--------------------------------------+
    5. | server_uuid | 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b |
    6. +---------------+--------------------------------------+
    7. 1 row in set (0.01 sec)

    master: 

    1. mysql> show master status ;
    2. +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
    3. | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    4. +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
    5. | master-bin.000004 | 194 | test,test1 | mysql,performance_schema,information_schema,sys | 21395c77-16b5-11ed-b628-005056b93c30:1-8 |
    6. +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
    7. 1 row in set (0.00 sec)

     slave:

    1. show slave status
    2. Retrieved_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8
    3. Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
    4. 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-3

    其中主库的Executed_Gtid_Set为 21395c77-16b5-11ed-b628-005056b93c30:1-8 

    可以看见Retrieved_Gtid_Set:21395c77-16b5-11ed-b628-005056b93c30:1-8 ,Executed_Gtid_Set:21395c77-16b5-11ed-b628-005056b93c30:1-8  ,也就是说主库产生了8个事务,从库接受到了来自主库的8个事务,并且都已经执行。

    其中21395c77-16b5-11ed-b628-005056b93c30是主库的server-uuid。那么我们可以解析从库的binlog再看看

    1. [root@localhost mysql]#
    2. [root@localhost mysql]# mysqlbinlog -vv master-bin.000004
    3. /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
    4. /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
    5. DELIMITER /*!*/;
    6. # at 4
    7. #220808 11:50:46 server id 1 end_log_pos 123 CRC32 0xe8ab61ad Start: binlog v 4, server v 5.7.35-log created 220808 11:50:46
    8. # Warning: this binlog is either in use or was not closed properly.
    9. BINLOG '
    10. FojwYg8BAAAAdwAAAHsAAAABAAQANS43LjM1LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    11. AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAXwAEGggAAAAICAgCAAAACgoKKioAEjQA
    12. Aa1hq+g=
    13. '/*!*/;
    14. # at 123
    15. #220808 11:50:46 server id 1 end_log_pos 194 CRC32 0x0e046fb9 Previous-GTIDs
    16. # 21395c77-16b5-11ed-b628-005056b93c30:1-8
    17. SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
    18. DELIMITER ;
    19. # End of log file
    20. /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
    21. /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

    那么对于文章开头那个诡异的gtid是怎么出来的呢?先说说已经执行的事务:

         Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
    56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-3

    这里的21395c77-16b5-11ed-b628-005056b93c30:1-8肯定很好理解,就是已经执行主库的1-8的事务,那么56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-3呢?这个其实也简单,有两种情况:

    第一种情况:从库有数据写入( 从库插入数据 )

    1. mysql> show slave status\G;
    2. *************************** 1. row ***************************
    3. Slave_IO_State: Waiting for master to send event
    4. Master_Host: 172.16.10.1
    5. Master_User: root
    6. Master_Port: 3306
    7. Connect_Retry: 60
    8. Master_Log_File: master-bin.000004
    9. Read_Master_Log_Pos: 194
    10. Relay_Log_File: localhost-relay-bin.000009
    11. Relay_Log_Pos: 409
    12. Relay_Master_Log_File: master-bin.000004
    13. Slave_IO_Running: Yes
    14. Slave_SQL_Running: Yes
    15. Replicate_Do_DB:
    16. Replicate_Ignore_DB:
    17. Replicate_Do_Table:
    18. Replicate_Ignore_Table:
    19. Replicate_Wild_Do_Table:
    20. Replicate_Wild_Ignore_Table:
    21. Last_Errno: 0
    22. Last_Error:
    23. Skip_Counter: 0
    24. Exec_Master_Log_Pos: 194
    25. Relay_Log_Space: 708
    26. Until_Condition: None
    27. Until_Log_File:
    28. Until_Log_Pos: 0
    29. Master_SSL_Allowed: No
    30. Master_SSL_CA_File:
    31. Master_SSL_CA_Path:
    32. Master_SSL_Cert:
    33. Master_SSL_Cipher:
    34. Master_SSL_Key:
    35. Seconds_Behind_Master: 0
    36. Master_SSL_Verify_Server_Cert: No
    37. Last_IO_Errno: 0
    38. Last_IO_Error:
    39. Last_SQL_Errno: 0
    40. Last_SQL_Error:
    41. Replicate_Ignore_Server_Ids:
    42. Master_Server_Id: 1
    43. Master_UUID: 21395c77-16b5-11ed-b628-005056b93c30
    44. Master_Info_File: /var/lib/mysql/master.info
    45. SQL_Delay: 0
    46. SQL_Remaining_Delay: NULL
    47. Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
    48. Master_Retry_Count: 86400
    49. Master_Bind:
    50. Last_IO_Error_Timestamp:
    51. Last_SQL_Error_Timestamp:
    52. Master_SSL_Crl:
    53. Master_SSL_Crlpath:
    54. Retrieved_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8
    55. Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
    56. 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-3
    57. Auto_Position: 1
    58. Replicate_Rewrite_DB:
    59. Channel_Name:
    60. Master_TLS_Version:
    61. 1 row in set (0.00 sec)
    62. ERROR:
    63. No query specified
    64. mysql> select * from jettech01;
    65. +------+------+
    66. | id | name |
    67. +------+------+
    68. | 1 | a |
    69. | 2 | b |
    70. | 3 | c |
    71. | 5 | f |
    72. | 7 | k |
    73. +------+------+
    74. 5 rows in set (0.00 sec)
    75. mysql> insert into jettech01 value(8,'l');
    76. Query OK, 1 row affected (0.00 sec)
    77. mysql> select * from jettech01;
    78. +------+------+
    79. | id | name |
    80. +------+------+
    81. | 1 | a |
    82. | 2 | b |
    83. | 3 | c |
    84. | 5 | f |
    85. | 7 | k |
    86. | 8 | l |
    87. +------+------+
    88. 6 rows in set (0.00 sec)
    89. mysql> show slave status\G;
    90. *************************** 1. row ***************************
    91. Slave_IO_State: Waiting for master to send event
    92. Master_Host: 172.16.10.1
    93. Master_User: root
    94. Master_Port: 3306
    95. Connect_Retry: 60
    96. Master_Log_File: master-bin.000004
    97. Read_Master_Log_Pos: 194
    98. Relay_Log_File: localhost-relay-bin.000009
    99. Relay_Log_Pos: 409
    100. Relay_Master_Log_File: master-bin.000004
    101. Slave_IO_Running: Yes
    102. Slave_SQL_Running: Yes
    103. Replicate_Do_DB:
    104. Replicate_Ignore_DB:
    105. Replicate_Do_Table:
    106. Replicate_Ignore_Table:
    107. Replicate_Wild_Do_Table:
    108. Replicate_Wild_Ignore_Table:
    109. Last_Errno: 0
    110. Last_Error:
    111. Skip_Counter: 0
    112. Exec_Master_Log_Pos: 194
    113. Relay_Log_Space: 708
    114. Until_Condition: None
    115. Until_Log_File:
    116. Until_Log_Pos: 0
    117. Master_SSL_Allowed: No
    118. Master_SSL_CA_File:
    119. Master_SSL_CA_Path:
    120. Master_SSL_Cert:
    121. Master_SSL_Cipher:
    122. Master_SSL_Key:
    123. Seconds_Behind_Master: 0
    124. Master_SSL_Verify_Server_Cert: No
    125. Last_IO_Errno: 0
    126. Last_IO_Error:
    127. Last_SQL_Errno: 0
    128. Last_SQL_Error:
    129. Replicate_Ignore_Server_Ids:
    130. Master_Server_Id: 1
    131. Master_UUID: 21395c77-16b5-11ed-b628-005056b93c30
    132. Master_Info_File: /var/lib/mysql/master.info
    133. SQL_Delay: 0
    134. SQL_Remaining_Delay: NULL
    135. Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
    136. Master_Retry_Count: 86400
    137. Master_Bind:
    138. Last_IO_Error_Timestamp:
    139. Last_SQL_Error_Timestamp:
    140. Master_SSL_Crl:
    141. Master_SSL_Crlpath:
    142. Retrieved_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8
    143. Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
    144. 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4
    145. Auto_Position: 1
    146. Replicate_Rewrite_DB:
    147. Channel_Name:
    148. Master_TLS_Version:
    149. 1 row in set (0.00 sec)
    150. ERROR:
    151. No query specified

    可以看见已经执行的事务有来自主库的Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,也有从库刚自己写入的数据:56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4。我们可以解析binlog看看。看从库的bin-log日志。

    1. [root@localhost mysql]# ls -al localhost-relay-bin.* slave36-bin.*
    2. -rw-r-----. 1 mysql mysql 299 511 11:50 localhost-relay-bin.000008
    3. -rw-r-----. 1 mysql mysql 409 511 11:50 localhost-relay-bin.000009
    4. -rw-r-----. 1 mysql mysql 58 511 11:50 localhost-relay-bin.index
    5. -rw-r-----. 1 mysql mysql 177 511 11:06 slave36-bin.000001
    6. -rw-r-----. 1 mysql mysql 177 511 11:06 slave36-bin.000002
    7. -rw-r-----. 1 mysql mysql 2941 511 14:41 slave36-bin.000003
    8. -rw-r-----. 1 mysql mysql 63 511 11:06 slave36-bin.index

    localhost-relay-bin.000009:从库执行主库同步过来的bin-log

    slave36-bin.000003:从库自己的bin-log日志 

    1. [root@localhost mysql]# mysqlbinlog -vv slave36-bin.000003 --include-gtids="56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4"
    2. #220511 14:41:38 server id 2 end_log_pos 2741 CRC32 0x161748e6 GTID last_committed=11 sequence_number=12 rbr_only=yes
    3. /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
    4. SET @@SESSION.GTID_NEXT= '56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:4'/*!*/;
    5. # at 2741
    6. #220511 14:41:38 server id 2 end_log_pos 2813 CRC32 0xa2271211 Query thread_id=3 exec_time=0 error_code=0
    7. SET TIMESTAMP=1652251298/*!*/;
    8. BEGIN
    9. /*!*/;
    10. # at 2813
    11. #220511 14:41:38 server id 2 end_log_pos 2868 CRC32 0xd00023ca Table_map: `test`.`jettech01` mapped to number 108
    12. # at 2868
    13. #220511 14:41:38 server id 2 end_log_pos 2910 CRC32 0x7a951c9a Write_rows: table id 108 flags: STMT_END_F
    14. BINLOG '
    15. olp7YhMCAAAANwAAADQLAAAAAGwAAAAAAAEABHRlc3QACWpldHRlY2gwMQACA/4C/gEDyiMA0A==
    16. olp7Yh4CAAAAKgAAAF4LAAAAAGwAAAAAAAEAAgAC//wIAAAAAWyaHJV6
    17. '/*!*/;
    18. ### INSERT INTO `test`.`jettech01`
    19. ### SET
    20. ### @1=8 /* INT meta=0 nullable=1 is_null=0 */
    21. ### @2='l' /* STRING(1) meta=65025 nullable=1 is_null=0 */
    22. # at 2910
    23. #220511 14:41:38 server id 2 end_log_pos 2941 CRC32 0x6fd47737 Xid = 396
    24. COMMIT/*!*/;
    25. SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
    26. DELIMITER ;
    27. # End of log file
    28. /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
    29. /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
    30. [root@localhost mysql]# ^C

    从binlog中可以清楚的看到是从库进行了写入。下面说第二组情况

    第二种情况:主从切换(我这里使用MHA切换主从)

    1. Retrieved_Gtid_Set: 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4
    2. Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
    3. 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4

    可以看到在切换以后主库的server-id是2。这里的意思是接收到主库56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4,并且已经执行这个事务,那么这个事务其实就是之前在从库写入的那条数据。对于21395c77-16b5-11ed-b628-005056b93c30:1-8个是之前作为主库执行。如果此时在主库再插入1条数据,那么又会变化如下

    1. Retrieved_Gtid_Set: 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-5
    2. Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
    3. 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-5

    下面说说文章开头提到的gtid不连续的问题,类似21395c77-16b5-11ed-b628-005056b93c30:37-45,这个是由于binlog被清理以后导致的,我们可以测试一下。然后查看gtid_purged变量。
    binlog不可能永远驻留在服务上,需要定期进行清理(通过expire_logs_days可以控制定期清理间隔),否则迟早它会把磁盘用尽。gtid_purged用于记录已经被清除了的binlog事务集合,它是gtid_executed的子集。只有gtid_executed为空时才能手动设置该变量,此时会同时更新gtid_executed为和gtid_purged相同的值。gtid_executed为空意味着要么之前没有启动过基于GTID的复制,要么执行过RESET MASTER。执行RESET MASTER时同样也会把gtid_purged置空,即始终保持gtid_purged是gtid_executed的子集。

    slave,从库:

    1. mysql> show master logs;
    2. +--------------------+-----------+
    3. | Log_name | File_size |
    4. +--------------------+-----------+
    5. | slave36-bin.000001 | 177 |
    6. | slave36-bin.000002 | 177 |
    7. | slave36-bin.000003 | 2941 |
    8. +--------------------+-----------+
    9. 3 rows in set (0.00 sec)
    10. mysql> flush logs;
    11. Query OK, 0 rows affected (0.00 sec)
    12. mysql> show master logs;
    13. +--------------------+-----------+
    14. | Log_name | File_size |
    15. +--------------------+-----------+
    16. | slave36-bin.000001 | 177 |
    17. | slave36-bin.000002 | 177 |
    18. | slave36-bin.000003 | 2990 |
    19. | slave36-bin.000004 | 234 |
    20. +--------------------+-----------+
    21. 4 rows in set (0.00 sec)
    22. mysql> PURGE BINARY LOGS TO 'slave36-bin.000004';
    23. Query OK, 0 rows affected (0.00 sec)
    24. mysql> show master logs;
    25. +--------------------+-----------+
    26. | Log_name | File_size |
    27. +--------------------+-----------+
    28. | slave36-bin.000004 | 234 |
    29. +--------------------+-----------+
    30. 1 row in set (0.00 sec)

    然后只要从库有重新启动,才会读取。MySQL服务器启动时,通过读binlog文件,初始化gtid_executed和gtid_purged,使它们的值能和上次MySQL运行时一致。

    gtid_executed被设置为最新的binlog文件中Previous_gtids_log_event和所有Gtid_log_event的并集。
    gtid_purged为最老的binlog文件中Previous_gtids_log_event。

    没启动前:

    1. Retrieved_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8
    2. Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
    3. 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4

    重启以后并且插入数据:

    1. Retrieved_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8
    2. Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
    3. 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-5
    1. mysql> show variables like 'gtid_purged';
    2. +---------------+------------------------------------------------------------------------------------+
    3. | Variable_name | Value |
    4. +---------------+------------------------------------------------------------------------------------+
    5. | gtid_purged | 21395c77-16b5-11ed-b628-005056b93c30:1-8,
    6. 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4 |
    7. +---------------+------------------------------------------------------------------------------------+
    8. 1 row in set (0.01 sec)

    到这里相信聪明的你一定看懂了。最后顺道说说gtid跳过复制错误的方法,对于跳过一个错误,找到无法执行事务的编号,比如是21395c77-16b5-11ed-b628-005056b93c30:1-8,那么操作如下:

    1. stop slave;
    2. set gtid_next='21395c77-16b5-11ed-b628-005056b93c30:1-8';
    3. begin;
    4. commit;
    5. set gtid_next='AUTOMATIC';
    6. start slave;

    上面方法只能跳过一个事务,那么对于一批如何跳过?在主库执行show master status,看主库执行到了哪里,比如:21395c77-16b5-11ed-b628-005056b93c30:1-33,那么操作如下:

    1. stop slave;
    2. reset master;
    3. set global gtid_purged=' 21395c77-16b5-11ed-b628-005056b93c30:1-33';
    4. start slave;

    gtid_executed:
    如何查看已经执行过的GTID?

    系统表 mysql.gtid_executed 存放了所有执行过的GTID(在活动的binlog中的除外),但是由于不包含活动的binlog当中的GTID,因此需要查看精确值时,可以查看 global variable gtid_executed 的值,这个变量的值是准确的(或者 show master status )。

    同一个GTID的事务不会在一个Server上执行两次,可以保证数据一致性。

    GTID SET
    GTID SET是指多个GTID的集合,示例如下:
    2174B383-5441-11E8-B90A-C80AA9429562:1-3, 24DA167-0C0C-11E8-8442-00059A3C7B00:1-19

    gtid_executed 和 gtid_purged 这两个系统变量 都是 GTID SET


    gtid_purged:
    这个一个 GTID SET,包含了所有已经提交过的,但是不在 binlog 当中的 GTID ,它是 gtid_executed 的子集。以下几种 GTID 都会添加到 gtid_purged 当中:

    1. 未开启binlog的从库上提交过的GTID
    2. 已经被 "purge" 掉的 binlog 当中的 GTID 。(当发出 purge binary log 命令之后,如果被 purge的binlog中包含有GTID,那么查看 gtid_purged 变量值的时候,就会看到该变量值发生了变化)
    3. 使用 'set global gtid_purged= "xxxx" ' 添加的 GTID
    什么时候需要设置 gtid_purged 变量?
    人为设置 gtid_purged 的目地是为了告诉服务器,即使它们不在 binlog 中,这些 GTID 已经 被应用过了,不能/不需要再重做。一个必须人为设置 gtid_purged 的场景见文章

    总结

    relay_log_recover=0

    • slave会扫描最后一个relay log文件,Retrieved_Gtid_Set显示的是当前扫描所得的GTID;io线程会通过扫描所得的最后一个GTID+1(如果Retrieved_Gtid_Set>=Executed_Gtid_Set)为依据来拉取,如果Retrieved_Gtid_Set

    relay_log_recover=1

    • slave中把所有relay log清除,io线程通过Executed_Gtid_Set后的+1个事务开始拉取并生成新的relay log文件;SQL线程在清除relay log时把Relay_Log_File、Relay_Log_Pos设为空,所以SQL线程从新的relay log文件的第一个事务开始应用

    1.2  产生:

    当一个事务提交时,就会分配一个GTID(前提是事务有写入到binlog),GTID单调递增且连续。

    1.3 GTID格式:

    GTID = source_id:transaction_id
    其中 source_id 一般指 source 的  server_uuid , 示例:3E11FA47-71CA-11E1-9E33-C80AA9429562:2

    • mysql.gtid_executed表:GTID持久化的介质,MySQL启动阶段会读取这个表来获取gtid_executed变量的值。
    • gtid_executed变量(show global variables):MySQL数据库已经执行了哪些GTID事务,处于内存中。show slave status中的executed_gtid_set也取自这里。
      1. mysql> show global variables like '%gtid%';
      2. +----------------------------------+------------------------------------------+
      3. | Variable_name | Value |
      4. +----------------------------------+------------------------------------------+
      5. | binlog_gtid_simple_recovery | ON |
      6. | enforce_gtid_consistency | ON |
      7. | gtid_executed | 21395c77-16b5-11ed-b628-005056b93c30:1-8 |
      8. | gtid_executed_compression_period | 1000 |
      9. | gtid_mode | ON |
      10. | gtid_owned | |
      11. | gtid_purged | |
      12. | session_track_gtids | OFF |
      13. +----------------------------------+------------------------------------------+
      14. 8 rows in set (0.01 sec)
      1. mysql> show master status ;
      2. +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
      3. | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
      4. +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
      5. | master-bin.000004 | 194 | test,test1 | mysql,performance_schema,information_schema,sys | 21395c77-16b5-11ed-b628-005056b93c30:1-8 |
      6. +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
      7. 1 row in set (0.00 sec)

    • gtid_purged变量(show global variables):由于BINLOG文件的删除(如purge binary logfiles或者超过expire_logs_days设置)已经丢失的GTID事务,同时在搭建备库的我们使用set global gtid_purged变量来提示MySQL哪些GTID事务我已经执行过了

    这也是我们DBA通常能够观察到的几种GTID,有了前文的描述我们知道其中mysql.gtid_executed表是一种GTID持久化的介质,而gtid_executed变量和gtid_purged变量则对应了,gtid_state中的executed_gtidslost_gtids内存数据。他们分别表示MySQL数据库执行了哪些GTID事务,有哪些GTID事务由于BINLOG文件的删除已经丢失了。

    其次我们先来达成一个共识gtid_executed变量一定是实时更新的不管主库和从库。我们的讨论分为主库,从库和通用从源码的角度进行详细讨论。并且约定都是打开GTID的情况下。最后给出最终总结。

    1.4 gtid_executed,全局参数,GTID集合包含所有在该服务器上执行过的事务编号和使用set gtid_purged语句设置过的事务编号,使用SHOW MASTER STATUS和SHOW SLAVE STATUS命令得到的Executed_Gtid_Set列值就取自于全局参数gitd_executed。
     
    gtid_purged,全局参数,GTID集合包含从binlog中purged掉的事务ID,该集合是全局参数gtid_executed的子集。

    2 参数更新机制

    2.1 master: 当复制主库关闭binlog时:
    1. 事务提交不会生成GTID,mysql.gtid_executed表/gtid_executed变量/gtid_purged变量均不更新。

    2.2 当复制主库开启binlog时:
    1. 事务提交需要生成Binlog,GTID在Binlog的ordered_commit flush阶段生成。
    2. 表mysql.gtid_executed在实例重启或flush log或binlog文件写满等造成binlog发生切换是保存上一个binlog执行过的全部gtid,属于非实时更新。
    3. 全局变量gtid_executed在事务commit阶段更新,属于实时更新。
    4. 全局变量gtid_purged在执行purge binary log命令或binlog超过保持期进行清理binlog时更新,属于非实时更新。

    2.3 当复制从库关闭binlog或关闭log_slave_update时:slave

    1. 在从库上应用主库binlog时不会生成新的GTID,也不会写入复制从库的binlog文件。
    2. 表mysql.gtid_executed在应用主库binlog事务时更新,并与事务一起提交,属于实时更新。
    3. 全局变量gtid_executed在主库binlog事务commit阶段更新,属于实时更新。
    4. 全局变量gtid_purged在主库binlog事务commit阶段更新,属于实时更新。

    2.4 当复制从库开启binlog或开启log_slave_update时:
    1. 在从库上应用主库binlog时不会生成新的GTID,但会写入复制从库的binlog文件。
    2. 表mysql.gtid_executed在实例重启或flush log或binlog文件写满等造成binlog发生切换是保存上一个binlog执行过的全部gtid,属于非实时更新。
    3. 全局变量gtid_executed在事务commit阶段更新,属于实时更新。
    4. 全局变量gtid_purged在执行purge binary log命令或binlog超过保持期进行清理binlog时更新,属于非实时更新。

    3 参数重置机制

    在某些场景下,需要修改全局变量gtid_purged和gtid_executed的值,执行对全局变量gtid_purged进行赋值时,会报以下错误:
    ERROR 1840 (HY000): @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.

    设置@@GLOBAL.GTID_EXECUTED需要通过RESET MASTER命令,该命令会清空当前服务器上的Binlog文件,并将@@GLOBAL.GTID_EXECUTED和@@GLOBAL.GTID_PURGED的值重置为空,重新初始化binlog文件序号,重新初始化GTID的事务ID起始值.

    4 参数恢复机制

    参数binlog_gtid_simple_recovery用于控制在实例重启时,如何计算全局变量gtid_executed和gtid_purged。在MySQL 5.7.7及之后版本中该参数默认为True。

    binlog_gtid_simple_recovery=FALSE

    • To initialize gtid_executed, binary log files are iterated from the newest file, stopping at the first binary log that has any Previous_gtids_log_event. All GTIDs from Previous_gtids_log_event and Gtid_log_events are read from this binary log file. This GTID set is stored internally and called gtids_in_binlog. The value of gtid_executed is computed as the union of this set and the GTIDs stored in the mysql.gtid_executed table.
    This process could take a long time if you had a large number of binary log files without GTID events, for example created when gtid_mode=OFF.
    • To initialize gtid_purged, binary log files are iterated from the oldest to the newest, stopping at the first binary log that contains either a Previous_gtids_log_event that is non-empty (that has at least one GTID) or that has at least one Gtid_log_event. From this binary log it reads Previous_gtids_log_event. This GTID set is subtracted from gtids_in_binlog and the result stored in the internal variable gtids_in_binlog_not_purged. The value of gtid_purged is initialized to the value of gtid_executed, minus gtids_in_binlog_not_purged.

    binlog_gtid_simple_recovery=TRUE
    which is the default in MySQL 5.7.7 and later, the server iterates only the oldest and the newest binary log files and the values of gtid_purged and gtid_executed are computed based only on Previous_gtids_log_event or Gtid_log_event found in these files. This ensures only two binary log files are iterated during server restart or when binary logs are being purged.

    在实例重启后,全局变量gtid_executed和gtid_purged的值取决于:

    1、从binlog文件头读取Previous_gtids_log_event获取该binlog之前的gtid_set

    2、从binlog文件的所有事件中获取该binlog中包含的gtid_set
    3、从mysql.gtid_executed表获取到执行过的gtid_set
    通过上面三个集合算出当前实例执行过的gtid_set和当前BINLOG中包含的gtid_set。

    4 mysql.gtid_executed表更新机制

    在MySQL 5.6版本中,开启GTID模式必须打开参数log_slave_updates,即从库必须在记录一份binlog,以保证在从库重启后,可以通过扫描最后一个二进制日志获得当前从库执行过的GTID集合。
    在MySQL 5.7.5版本中引入mysql.gtid_executed表来存放gtid信息,因此在GTID模式下不要求开启log_slave_update参数。
     
    在MySQL 5.7版本中,对mysql.gtid_executed表的更新策略分为:

    1、主库服务器,未开启log_bin参数,则不对mysql.gtid_executed表进行更新。
    2、主库服务器,开启log_bin参数,当二进制文件进行rotation时或者关闭实例时对mysql.gtid_executed进行更新。
    3、从库服务器,未开启log_bin参数或未开启log_slave_updates参数,在用户事务执行时对mysql.gtid_executed进行更新,并与用户事务一起进行提交。
    4、从库服务器,开启log_bin参数和开启log_slave_updates参数,当二进制文件进行rotation时或者关闭实例时对mysql.gtid_executed进行更新。

    当主库开启log_bin参数或从库开启log_slave_update参数时,执行或应用事务所生成的binlog会写入磁盘,当二进制文件进行rotation时或者关闭实例时对mysql.gtid_executed进行更新,表mysql.gtid_executed中不能提供完整的GTID集合数据,需以参数gtid_executed为准,如果实例异常重启,最近的GTID集合数据没有更新到mysql.gtid_executed表中,当实例恢复时,通过扫描最后一个binlog文件来获得最新的GTID集合数据。

    当从库未开启log_bin参数或未开启log_slave_updates参数时,应用主库所传递过来的事务不会产生新的binlog,在执行事务开始便可以获取到该事务的GTID,因此可以随着事务一起提交,类似于MySQL 5.6版本中随事务一起更新mysql.slave_master_info表一样。

    当执行RESET MASTER时,表mysql.gtid_executed会被清空。


    5 mysql.gtid_executed表压缩

    当从库未开启log_slave_updates参数时,由于每个事务都会向mysql.gtid_executed表写入记录,为防止mysql.gtid_executed表数据量暴增,MySQL 5.7引入参数gtid_executed_compression_period来控制每执行N个事务后,对mysql.gtid_executed表进行一次压缩,将多个连续的gtid值合并为gtid集合。如果gtid_executed_compression_period被设置为0,则不会进行压缩。
    当主库开启log_bin参数或从库开启log_slave_update参数时,参数gtid_executed_compression_period并不会被使用,只有当二进制文件进行Rotation时才会进行GTID压缩。MySQL Replication--全局参数gtid_executed和gtid_purged - TeyGao - 博客园

    ==================================

    1.

    1. #从库通过show slave status\G找到冲突的GTID号(如上图)
    2. stop slave sql_thread;
    3. SET gtid_next = '冲突的GTID号';
    4. #SET gtid_next = '00017261-1111-1111-1111-111111111111:858';
    5. 之后的变化为Executed_Gtid_Set: 00017261-1111-1111-1111-111111111111:1-858,
    6. BEGIN;COMMIT;
    7. SET gtid_next = 'AUTOMATIC';
    8. start slave sql_thread;
    9. #最后从库再次执行 show slave status\G 进行验证。

    2.gtid_next与GLOBAL GTID_PURGED:小知识分享

    相同点:
    都是改变Executed_Gtid_Set的值。(表示为从库已经执行完毕的事务gtid信息)
    不同点:
    gtid_next是一个具体的gtid号(影响范围小)

    GLOBAL GTID_PURGED是一段gtid号,并且需要Executed_Gtid_Set为空,也就是需要执行reset master
    (影响范围大,比如说新建一个增量的从库,就可以告诉从库执行那些relaylog,有这个场景但没实践过)
    #配合以下语句使用
    show variables like '%gtid_purged%';#查看从库执行过的信息
    (reset master之后为空,从库的binlog也会重建)
    #SET GLOBAL GTID_PURGED='1111:1-850'
    #手动让从库认为1111:845已经执行

    3.恢复报错:

    ERROR 1840 (HY000) at line 24: @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty

    当前GTID_EXECUTED参数已经有值,而从集群倒出来的dump文件中包含了SET @@GLOBAL.GTID_PURGED的操作
    

    4.解决方法

    1. 方法三:vim all_db.sql (记录下图的set信息,然后删除set值,重新恢复单库)
    2. (注意备份文件的大小,越大打开的越慢)
    3. 方法二:reset master
    4. 这个操作可以将当前库的GTID_EXECUTED值置空
    5. 方法一:--set-gtid-purged=off
    6. 在dump导出时,添加--set-gtid-purged=off参数,避免将gtid信息导出
    7. mysqldump -uroot -p --set-gtid-purged=off -d test> test.sql

  • 相关阅读:
    【从入门到入土】Java SE 反射机制1(从JDBC回来的!!)
    低代码:对开发人员可行吗
    系列十、ReentrantReadWriteLock
    UEC++ 接口
    网工老大难经典提问之“IE要到期如何续证?”关于思科CCIE【重认证】常用方式,这次请务必保留好!
    论文阅读:Detecting, Explaining, and Mitigating Memorization in Diffusion Models
    二十一,结合直射光和间接光绘制小球
    LeetCode动态规划编辑距离问题——583. 两个字符串的删除操作
    快解析——好用的内网安全软件
    「造轮子」一个简单的 RPC 框架(基于Netty+Zookeeper+Spring)
  • 原文地址:https://blog.csdn.net/Michaelwubo/article/details/126225861