centos7下mysql双主+keepalived - benjamin杨 - 博客园
请设置从库为只读read_only=1,super_read_only=1就会大概率避免下面的情况
1.1
gtid_executed等价Executed_Gtid_Set参数,已经执行的gtid集合(gtid-sets)。
gtid_purged,已经清除的gtid集合。
Retrieved_Gtid_Set:从库已经接收到主库的事务编号(从库的IO线程已经接受到了)
Executed_Gtid_Set:已经执行的事务编号(从库的执行sql线程已经执行了的sql)
master:
- mysql> show variables like '%uuid%';
- +---------------+--------------------------------------+
- | Variable_name | Value |
- +---------------+--------------------------------------+
- | server_uuid | 21395c77-16b5-11ed-b628-005056b93c30 |
- +---------------+--------------------------------------+
- 1 row in set (0.01 sec)
slave:
- mysql> show variables like '%uuid%';
- +---------------+--------------------------------------+
- | Variable_name | Value |
- +---------------+--------------------------------------+
- | server_uuid | 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b |
- +---------------+--------------------------------------+
- 1 row in set (0.01 sec)
master:
- mysql> show master status ;
- +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
- | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
- +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
- | master-bin.000004 | 194 | test,test1 | mysql,performance_schema,information_schema,sys | 21395c77-16b5-11ed-b628-005056b93c30:1-8 |
- +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
- 1 row in set (0.00 sec)
slave:
- show slave status
-
- Retrieved_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8
- Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
- 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再看看
- [root@localhost mysql]#
- [root@localhost mysql]# mysqlbinlog -vv master-bin.000004
- /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
- /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
- DELIMITER /*!*/;
- # at 4
- #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
- # Warning: this binlog is either in use or was not closed properly.
- BINLOG '
- FojwYg8BAAAAdwAAAHsAAAABAAQANS43LjM1LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAXwAEGggAAAAICAgCAAAACgoKKioAEjQA
- Aa1hq+g=
- '/*!*/;
- # at 123
- #220808 11:50:46 server id 1 end_log_pos 194 CRC32 0x0e046fb9 Previous-GTIDs
- # 21395c77-16b5-11ed-b628-005056b93c30:1-8
- SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
- DELIMITER ;
- # End of log file
- /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
- /*!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呢?这个其实也简单,有两种情况:
第一种情况:从库有数据写入( 从库插入数据 )
- mysql> show slave status\G;
- *************************** 1. row ***************************
- Slave_IO_State: Waiting for master to send event
- Master_Host: 172.16.10.1
- Master_User: root
- Master_Port: 3306
- Connect_Retry: 60
- Master_Log_File: master-bin.000004
- Read_Master_Log_Pos: 194
- Relay_Log_File: localhost-relay-bin.000009
- Relay_Log_Pos: 409
- Relay_Master_Log_File: master-bin.000004
- 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: 194
- Relay_Log_Space: 708
- 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: 1
- Master_UUID: 21395c77-16b5-11ed-b628-005056b93c30
- 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: 21395c77-16b5-11ed-b628-005056b93c30:1-8
- Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
- 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-3
- Auto_Position: 1
- Replicate_Rewrite_DB:
- Channel_Name:
- Master_TLS_Version:
- 1 row in set (0.00 sec)
-
- ERROR:
- No query specified
-
-
-
- mysql> select * from jettech01;
- +------+------+
- | id | name |
- +------+------+
- | 1 | a |
- | 2 | b |
- | 3 | c |
- | 5 | f |
- | 7 | k |
- +------+------+
- 5 rows in set (0.00 sec)
-
- mysql> insert into jettech01 value(8,'l');
- Query OK, 1 row affected (0.00 sec)
-
- mysql> select * from jettech01;
- +------+------+
- | id | name |
- +------+------+
- | 1 | a |
- | 2 | b |
- | 3 | c |
- | 5 | f |
- | 7 | k |
- | 8 | l |
- +------+------+
- 6 rows in set (0.00 sec)
-
- mysql> show slave status\G;
- *************************** 1. row ***************************
- Slave_IO_State: Waiting for master to send event
- Master_Host: 172.16.10.1
- Master_User: root
- Master_Port: 3306
- Connect_Retry: 60
- Master_Log_File: master-bin.000004
- Read_Master_Log_Pos: 194
- Relay_Log_File: localhost-relay-bin.000009
- Relay_Log_Pos: 409
- Relay_Master_Log_File: master-bin.000004
- 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: 194
- Relay_Log_Space: 708
- 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: 1
- Master_UUID: 21395c77-16b5-11ed-b628-005056b93c30
- 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: 21395c77-16b5-11ed-b628-005056b93c30:1-8
- Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
- 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4
- Auto_Position: 1
- Replicate_Rewrite_DB:
- Channel_Name:
- Master_TLS_Version:
- 1 row in set (0.00 sec)
-
- ERROR:
- No query specified
可以看见已经执行的事务有来自主库的Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,也有从库刚自己写入的数据:56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4。我们可以解析binlog看看。看从库的bin-log日志。
- [root@localhost mysql]# ls -al localhost-relay-bin.* slave36-bin.*
- -rw-r-----. 1 mysql mysql 299 5月 11 11:50 localhost-relay-bin.000008
- -rw-r-----. 1 mysql mysql 409 5月 11 11:50 localhost-relay-bin.000009
- -rw-r-----. 1 mysql mysql 58 5月 11 11:50 localhost-relay-bin.index
- -rw-r-----. 1 mysql mysql 177 5月 11 11:06 slave36-bin.000001
- -rw-r-----. 1 mysql mysql 177 5月 11 11:06 slave36-bin.000002
- -rw-r-----. 1 mysql mysql 2941 5月 11 14:41 slave36-bin.000003
- -rw-r-----. 1 mysql mysql 63 5月 11 11:06 slave36-bin.index
localhost-relay-bin.000009:从库执行主库同步过来的bin-log
slave36-bin.000003:从库自己的bin-log日志
- [root@localhost mysql]# mysqlbinlog -vv slave36-bin.000003 --include-gtids="56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4"
-
- #220511 14:41:38 server id 2 end_log_pos 2741 CRC32 0x161748e6 GTID last_committed=11 sequence_number=12 rbr_only=yes
- /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
- SET @@SESSION.GTID_NEXT= '56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:4'/*!*/;
- # at 2741
- #220511 14:41:38 server id 2 end_log_pos 2813 CRC32 0xa2271211 Query thread_id=3 exec_time=0 error_code=0
- SET TIMESTAMP=1652251298/*!*/;
- BEGIN
- /*!*/;
- # at 2813
- #220511 14:41:38 server id 2 end_log_pos 2868 CRC32 0xd00023ca Table_map: `test`.`jettech01` mapped to number 108
- # at 2868
- #220511 14:41:38 server id 2 end_log_pos 2910 CRC32 0x7a951c9a Write_rows: table id 108 flags: STMT_END_F
-
- BINLOG '
- olp7YhMCAAAANwAAADQLAAAAAGwAAAAAAAEABHRlc3QACWpldHRlY2gwMQACA/4C/gEDyiMA0A==
- olp7Yh4CAAAAKgAAAF4LAAAAAGwAAAAAAAEAAgAC//wIAAAAAWyaHJV6
- '/*!*/;
- ### INSERT INTO `test`.`jettech01`
- ### SET
- ### @1=8 /* INT meta=0 nullable=1 is_null=0 */
- ### @2='l' /* STRING(1) meta=65025 nullable=1 is_null=0 */
- # at 2910
- #220511 14:41:38 server id 2 end_log_pos 2941 CRC32 0x6fd47737 Xid = 396
- COMMIT/*!*/;
- SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
- DELIMITER ;
- # End of log file
- /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
- /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
- [root@localhost mysql]# ^C
从binlog中可以清楚的看到是从库进行了写入。下面说第二组情况
第二种情况:主从切换(我这里使用MHA切换主从)
- Retrieved_Gtid_Set: 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4
- Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
- 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条数据,那么又会变化如下
- Retrieved_Gtid_Set: 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-5
- Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
- 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,从库:
- mysql> show master logs;
- +--------------------+-----------+
- | Log_name | File_size |
- +--------------------+-----------+
- | slave36-bin.000001 | 177 |
- | slave36-bin.000002 | 177 |
- | slave36-bin.000003 | 2941 |
- +--------------------+-----------+
- 3 rows in set (0.00 sec)
-
- mysql> flush logs;
- Query OK, 0 rows affected (0.00 sec)
-
- mysql> show master logs;
- +--------------------+-----------+
- | Log_name | File_size |
- +--------------------+-----------+
- | slave36-bin.000001 | 177 |
- | slave36-bin.000002 | 177 |
- | slave36-bin.000003 | 2990 |
- | slave36-bin.000004 | 234 |
- +--------------------+-----------+
- 4 rows in set (0.00 sec)
-
- mysql> PURGE BINARY LOGS TO 'slave36-bin.000004';
- Query OK, 0 rows affected (0.00 sec)
-
- mysql> show master logs;
- +--------------------+-----------+
- | Log_name | File_size |
- +--------------------+-----------+
- | slave36-bin.000004 | 234 |
- +--------------------+-----------+
- 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。
没启动前:
- Retrieved_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8
- Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
- 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4
重启以后并且插入数据:
- Retrieved_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8
- Executed_Gtid_Set: 21395c77-16b5-11ed-b628-005056b93c30:1-8,
- 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-5
- mysql> show variables like 'gtid_purged';
- +---------------+------------------------------------------------------------------------------------+
- | Variable_name | Value |
- +---------------+------------------------------------------------------------------------------------+
- | gtid_purged | 21395c77-16b5-11ed-b628-005056b93c30:1-8,
- 56fc5c7d-d0d7-11ec-bbf9-005056b9e62b:1-4 |
- +---------------+------------------------------------------------------------------------------------+
- 1 row in set (0.01 sec)
到这里相信聪明的你一定看懂了。最后顺道说说gtid跳过复制错误的方法,对于跳过一个错误,找到无法执行事务的编号,比如是21395c77-16b5-11ed-b628-005056b93c30:1-8,那么操作如下:
- stop slave;
- set gtid_next='21395c77-16b5-11ed-b628-005056b93c30:1-8';
- begin;
- commit;
- set gtid_next='AUTOMATIC';
- start slave;
上面方法只能跳过一个事务,那么对于一批如何跳过?在主库执行show master status,看主库执行到了哪里,比如:21395c77-16b5-11ed-b628-005056b93c30:1-33,那么操作如下:
- stop slave;
- reset master;
- set global gtid_purged=' 21395c77-16b5-11ed-b628-005056b93c30:1-33';
- 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 的场景见文章
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> show global variables like '%gtid%';
- +----------------------------------+------------------------------------------+
- | Variable_name | Value |
- +----------------------------------+------------------------------------------+
- | binlog_gtid_simple_recovery | ON |
- | enforce_gtid_consistency | ON |
- | gtid_executed | 21395c77-16b5-11ed-b628-005056b93c30:1-8 |
- | gtid_executed_compression_period | 1000 |
- | gtid_mode | ON |
- | gtid_owned | |
- | gtid_purged | |
- | session_track_gtids | OFF |
- +----------------------------------+------------------------------------------+
- 8 rows in set (0.01 sec)
- mysql> show master status ;
- +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
- | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
- +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
- | master-bin.000004 | 194 | test,test1 | mysql,performance_schema,information_schema,sys | 21395c77-16b5-11ed-b628-005056b93c30:1-8 |
- +-------------------+----------+--------------+-------------------------------------------------+------------------------------------------+
- 1 row in set (0.00 sec)
这也是我们DBA通常能够观察到的几种GTID,有了前文的描述我们知道其中mysql.gtid_executed表是一种GTID持久化的介质,而gtid_executed变量和gtid_purged变量则对应了,gtid_state中的executed_gtids和lost_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.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时更新,属于非实时更新。
在某些场景下,需要修改全局变量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起始值.
参数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。
在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会被清空。
当从库未开启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.
- #从库通过show slave status\G找到冲突的GTID号(如上图)
- stop slave sql_thread;
- SET gtid_next = '冲突的GTID号';
- #SET gtid_next = '00017261-1111-1111-1111-111111111111:858';
- 之后的变化为Executed_Gtid_Set: 00017261-1111-1111-1111-111111111111:1-858,
- BEGIN;COMMIT;
- SET gtid_next = 'AUTOMATIC';
- start slave sql_thread;
- #最后从库再次执行 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.解决方法
- 方法三:vim all_db.sql (记录下图的set信息,然后删除set值,重新恢复单库)
- (注意备份文件的大小,越大打开的越慢)
-
- 方法二:reset master
- 这个操作可以将当前库的GTID_EXECUTED值置空
-
- 方法一:--set-gtid-purged=off
- 在dump导出时,添加--set-gtid-purged=off参数,避免将gtid信息导出
- mysqldump -uroot -p --set-gtid-purged=off -d test> test.sql