gtid

 

gtid知识:

 启用GTID:
需要设置三个参数:
gtid_mode=on
enforce_gtid_consistency=1
log_slave_updates=1

 

一、gtid持久化介质

 

01 、mysql.gtid_executed表:

work@master (mysql) > show create table gtid_executed\G;
*************************** 1. row ***************************
       Table: gtid_executed
Create Table: CREATE TABLE `gtid_executed` (
  `source_uuid` char(36) NOT NULL COMMENT 'uuid of the source where the transaction was originally executed.',
  `interval_start` bigint(20) NOT NULL COMMENT 'First number of interval.',
  `interval_end` bigint(20) NOT NULL COMMENT 'Last number of interval.',
  PRIMARY KEY (`source_uuid`,`interval_start`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 STATS_PERSISTENT=0
1 row in set (0.07 sec)

ERROR: 
No query specified

Mon May 11 16:56:12 2020

 

 

 

 02、binlog中的gtid_event

 

#  at  120
#151222  9:07:58 server id 1026872634  end_log_pos 247 CRC32 0xedf993a8     Previous-GTIDs
# b3485508-883f-11e5-85fb-e41f136aba3e:1-14,
# b694c8b2-883f-11e5-85fb-e41f136aba3e:1-10115960:12000000-12000005
#  at  247
#151222  9:08:03 server id 1026872625  end_log_pos 295 CRC32 0xc3d3d8ee     GTID [ commit =yes]
SET  @@SESSION.GTID_NEXT=  'b694c8b2-883f-11e5-85fb-e41f136aba3e:10115961' /*!*/;
#  at  295
#151222  9:08:03 server id 1026872625  end_log_pos 370 CRC32 0x0a32d229     Query   thread_id=18    exec_time=1 error_code=0
BEGIN
/*!*/;
#  at  370
#151222  9:08:03 server id 1026872625  end_log_pos 480 CRC32 0x3c0e094f     Query   thread_id=18    exec_time=1 error_code=0
use `db`/*!*/;
SET  TIMESTAMP =1450746483/*!*/;
update  tb  set  val = val + 1  where  id = 1
/*!*/;
#  at  480
#151222  9:08:03 server id 1026872625  end_log_pos 511 CRC32 0x5772f16b     Xid = 6813913
COMMIT /*!*/;
#  at  511
#151222  9:10:19 server id 1026872625  end_log_pos 559 CRC32 0x3ac30191     GTID [ commit =yes]
SET  @@SESSION.GTID_NEXT=  'b694c8b2-883f-11e5-85fb-e41f136aba3e:10115962' /*!*/;
#  at  559
#151222  9:10:19 server id 1026872625  end_log_pos 634 CRC32 0x83a74912     Query   thread_id=18    exec_time=0 error_code=0
SET  TIMESTAMP =1450746619/*!*/;
BEGIN
/*!*/;
#  at  634
#151222  9:10:19 server id 1026872625  end_log_pos 744 CRC32 0x581f6031     Query   thread_id=18    exec_time=0 error_code=0
SET  TIMESTAMP =1450746619/*!*/;
update  tb  set  val = val + 1  where  id = 1
/*!*/;
#  at  744
#151222  9:10:19 server id 1026872625  end_log_pos 775 CRC32 0x793f8e34     Xid = 6813916
COMMIT /*!*/;

 

 

 二、gtid相关变量和表

 

 

01、mysql.gtid_executed表

           含义:数据库已经执行了哪些Gtid事物,存储在表中。只有从库在binlog或log_slave_updates至少一个关闭时,才会实时更新的

 

1. 执行 reset master 时,则删除该表所有记录。

2. 执行 set global gitd_purged='' 时,则插入对应的记录到mysql.gtid_executed表,且同时更新gtid_executed为和gtid_purged相同的值,即始终保持gtid_purged是gtid_executed的子集。

3. 主库:若binlog关闭,则不更新;若binlog打开,则binlog发生rotate切换时,会更新该表数据。

4. 从库:若binlog或log_slave_updates至少一个关闭,则实时更新;若binlog和log_slave_updates都开启,则binlog发生rotate切换时,会更新该表数据。

 

 

 

 

02、gtid_executed变量

          含义:数据库已经执行了哪些Gtid事物,存储在内存中。

         show slave status中的Executed_Gtid_Set也取自这里。

         gtid_executed为空有两种情况:一是执行reset master;二是之前没有启动过基于GTID的复制。

1. 执行 reset master 时,则该变量设置为空。

2. 执行 set global gitd_purged='' 时,则插入对应的记录到mysql.gtid_executed表,且同时更新gtid_executed为和gtid_purged相同的值,即始终保持gtid_purged是gtid_executed的子集。

3. mysql启动时,通过读取mysql.gtid_executed表的数据来初始化gtid_executed变量。

4. 只有当主库在关闭binlog的时候,不会更新gtid_executed变量;其余情况,都会实时更新gtid_executed变量。

 

 

 

 

03、gitd_purged变量

           含义:全局变量。用于记录已经被清除了的binlog事务集合,gitd_purged变量是gtid_executed变量的子集。

           只有gtid_executed为空时,才能手动设置gitd_purged变量。

       

1. 执行 reset master 时,则该变量设置为空。

2. 执行 set global gitd_purged='XXX' 时,则插入对应的记录到mysql.gtid_executed表,且同时更新gtid_executed为和gtid_purged相同的值,即始终保持gtid_purged是gtid_executed的子集。
    
   用来来提示Mysql,哪些Gtid事务我已经执行过了。用于搭建从库时,使用极多。通常是先在从库执行:reset master;然后再在从库执行:set global gitd_purged='XXX',
 
   注意,这里的XXX需要结合该从库Retrieved_Gtid_Set和Executed_Gtid_Set的并集,还要加上主库的Executed_Gtid_Set中存在的,但不是该主库产生的gtid,且该从库没有的gtid。

   但是这样有个隐患,就是主库上有其他机器执行过的gtid而得到的数据,但是该从库却没有真正存在这些数据,而你在该从库上却设置为这些gtid事务是自己purge掉了,其实自己压根没有这些数据,这样就导致从库的数据少于主库,即丢掉了数据。

3. mysql启动时,通过读取mysql.gtid_executed表的数据来初始化gtid_executed变量。

4. 主库:若binlog关闭,则不更新gitd_purged变量;若binlog打开,则binlog被清理时,会更新gitd_purged变量。

5. 从库:若binlog或log_slave_updates至少一个关闭,则实时更新gitd_purged变量;若binlog和log_slave_updates都开启,则binlog被清理时,会更新gitd_purged变量。


########################################################################################################################################################################################

手动删除binlog:

   show binary logs;

   purge binary logs to 'mysql-bin.000046';

   purge binary logs before '2020-10-10 10:10:10';

自动删除binlog:

   expire_logs_days时间到了,执行binlog删除

 

 

          跳过gtid事务:

1)停止复制:
stop slave;
2)重置master:
reset master;
3)设置gtid_purged变量:

set global gtid_purged='XXX';
4)开启复制:
start slave;

 

 

3,使用空事务处理故障
情况1:
主键重复

#  Last_Error: Could not execute Write_rows event on table test.a; Duplicate entry '500' for key 'PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql.000005, end_log_pos 651107
# 方法1:
# 注入空事务

      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 7abe211e-fb4f-11e7-b16e-000c29ba88ca:66253-68381:69091-70362
            Executed_Gtid_Set: 7abe211e-fb4f-11e7-b16e-000c29ba88ca:1-70362,
9a455b69-fb4f-11e7-b993-000c29e8a43c:1
##########################
##########################

(root@Slave)[tempdb]>stop slave sql_thread;

(root@Slave)[tempdb]>set gtid_next='7abe211e-fb4f-11e7-b16e-000c29ba88ca:70363';

(root@Slave)[tempdb]>begin;commit;

(root@Slave)[tempdb]>set gtid_next='AUTOMATIC';

(root@Slave)[tempdb]>start slave sql_thread;

(root@Slave)[tempdb]>show slave status \G

 

 

 

 

 

 

 

 

04、Executed_Gtid_Set

           含义:

1. 在主库中,执行 show master status命令,所得Executed_Gtid_Set,就是和全局变量gitd_executed相同值,任何时候,两者完全相同值。

2. 在从库中,执行 show slave  status命令,所得Executed_Gtid_Set,就是和全局变量gitd_executed相同值,任何时候,两者完全相同值。

 

 

 

05、Retrieved_Gtid_Set

            含义:仅在从库中才有,在主库中不存在。

 

 

 

06、Previous-GTIDs

1. Previous_gtid_log_event在每个binlog 头部都会有每次binlog rotate的时候存储在binlog头部,Previous-GTIDs在binlog中只会存储在这台机器上执行过的所有binlog,不包括手动设置gtid_purged值。

换句话说,如果你手动set global gtid_purged
=xx; 那么xx是不会记录在Previous_gtid_log_event中的。

 

 

07、gtid_next变量

 

 

          跳过gtid事务:

 (1) 停止slave进程:
mysql
> stop slave; (2) 设置事务号,事务号从Retrieved_Gtid_Set获取: 在session里设置gtid_next,即跳过这个GTID mysql> SET @@sission.gtid_next= '8f9e146f-0a18-11e7-810a-0050568833c8:4'
(3)设置空事物 mysql> begin; commit;
4)恢复事物号 mysql> set session gtid_next= automatic; (5) 启动slave进程 mysql> start slave;

 

 

08、gtid_owned

 

08、gtid_mode

 

09、enforce_gtid_consistency

 

 

10、gtid_executed_compression_period

 

 

 

 

 

 

 

 

1、gtid配置

 MySQL通过全局变量gtid_mode控制开启/关闭GTID模式。但是gtid_mode是只读的,可添加到配置文件中,然后重启mysqld来开启GTID模式。相关配置项如下:
gtid-mode                = ON
enforce_gtid_consistency = 1
log-slave-updates        = 1
log-bin                  = mysql-bin
log-bin-index            = mysql-bin.index

 

 

 

 

2、查看配置

> show  global  variables  like  'gtid_%'\G;
*************************** 1. row ***************************
Variable_name: gtid_executed
        Value: 5a1a41db-9f15-11e9-a991-e4434b210720:1-2,
5aa95098-9f15-11e9-98f3-e4434b5a47f8:1-429,
5bcba8f5-9f15-11e9-9b14-e4434b210748:1-6319147104,
5d2b585f-9f15-11e9-a58d-e4434b2106e8:1-1373519,
62cdce4f-9f15-11e9-9f0d-e4434b21b430:1-2,
66b80dbd-c979-11e9-8582-246e96c58570:1,
85e1f282-f3a7-11e9-a222-e4434b2106e8:1-24
*************************** 2. row ***************************
Variable_name: gtid_executed_compression_period
        Value: 1000
*************************** 3. row ***************************
Variable_name: gtid_mode
        Value: ON
*************************** 4. row ***************************
Variable_name: gtid_owned
        Value:
*************************** 5. row ***************************
Variable_name: gtid_purged
        Value: 5a1a41db-9f15-11e9-a991-e4434b210720:1-2,
5aa95098-9f15-11e9-98f3-e4434b5a47f8:1-429,
5bcba8f5-9f15-11e9-9b14-e4434b210748:1-6047460531:6047460533,
5d2b585f-9f15-11e9-a58d-e4434b2106e8:1-1373519,
62cdce4f-9f15-11e9-9f0d-e4434b21b430:1-2,
66b80dbd-c979-11e9-8582-246e96c58570:1,
85e1f282-f3a7-11e9-a222-e4434b2106e8:1-22
5 rows in set (0.00 sec)

ERROR:
No query specified

Sun Dec  8 16:57:49 2019

 

这里有4个变量,其中gtid_mode已经介绍过了,其他3个变量的含义如下

        gtid_executed:这既是一个Global级别的变量,又是一个Session级别的变量,是只读变量。Global级别的gtid_executed表示当前实例已经执行过的GTID集合。Session级别的gtid_executed一般情况下是空的。

        gtid_owned:这既是一个Global级别的变量,又是一个Session级别的变量,是只读变量。Global级别的gtid_owned表示当前实例正在执行中的GTID,以及对应的线程id。Session级别的gtid_owned一般情况下是空的。

        gtid_purged:这是一个Global级别的变量,可动态修改。我们知道binlog可以被purge掉,gtid_purged表示当前实例中已经被purge掉的GTID集合,很明显gtid_purged是gtid_executed的子集。但是gtid_purged也不是可以随意修改的,必须在@@global.gtid_executed是空的情况下,才可以动态设置gtid_purged。

 

binlog文件中的gtid

#  at  120
#151222  9:07:58 server id 1026872634  end_log_pos 247 CRC32 0xedf993a8     Previous-GTIDs
# b3485508-883f-11e5-85fb-e41f136aba3e:1-14,
# b694c8b2-883f-11e5-85fb-e41f136aba3e:1-10115960:12000000-12000005
#  at  247
#151222  9:08:03 server id 1026872625  end_log_pos 295 CRC32 0xc3d3d8ee     GTID [ commit =yes]
SET  @@SESSION.GTID_NEXT=  'b694c8b2-883f-11e5-85fb-e41f136aba3e:10115961' /*!*/;
#  at  295
#151222  9:08:03 server id 1026872625  end_log_pos 370 CRC32 0x0a32d229     Query   thread_id=18    exec_time=1 error_code=0
BEGIN
/*!*/;
#  at  370
#151222  9:08:03 server id 1026872625  end_log_pos 480 CRC32 0x3c0e094f     Query   thread_id=18    exec_time=1 error_code=0
use `db`/*!*/;
SET  TIMESTAMP =1450746483/*!*/;
update  tb  set  val = val + 1  where  id = 1
/*!*/;
#  at  480
#151222  9:08:03 server id 1026872625  end_log_pos 511 CRC32 0x5772f16b     Xid = 6813913
COMMIT /*!*/;
#  at  511
#151222  9:10:19 server id 1026872625  end_log_pos 559 CRC32 0x3ac30191     GTID [ commit =yes]
SET  @@SESSION.GTID_NEXT=  'b694c8b2-883f-11e5-85fb-e41f136aba3e:10115962' /*!*/;
#  at  559
#151222  9:10:19 server id 1026872625  end_log_pos 634 CRC32 0x83a74912     Query   thread_id=18    exec_time=0 error_code=0
SET  TIMESTAMP =1450746619/*!*/;
BEGIN
/*!*/;
#  at  634
#151222  9:10:19 server id 1026872625  end_log_pos 744 CRC32 0x581f6031     Query   thread_id=18    exec_time=0 error_code=0
SET  TIMESTAMP =1450746619/*!*/;
update  tb  set  val = val + 1  where  id = 1
/*!*/;
#  at  744
#151222  9:10:19 server id 1026872625  end_log_pos 775 CRC32 0x793f8e34     Xid = 6813916
COMMIT /*!*/;

这段Binlog从文件120偏移处(Format_description_log_event之后的第一个Binlog Event)开始截取。可以看到,第一个Binlog Event的类型为:Previous-GTIDs,它存在于每个binlog文件中。当开启GTID时,每个binlog文件都有且只有一个Previous-GTIDs,位置都是在Format_description_log_event之后的第一个Binlog Event处。它的含义是在当前Binlog文件之前执行过的GTID集合,可以充当索引用,使用这个Binlog Event,可以便于快速判断GTID是否位于当前binlog文件中。

 下面看看gtid_purged和gtid_executed是如何构造的。MySQL在启动时打开最老的binlog文件,读取其中的Previous-GTIDs,那么就是@@global.gtid_purged。MySQL在启动时打开最新的binlog文件,读取其中的Previous-GTIDs,构造一个gtid_set,然后再遍历这个最新的binlog文件,把遇到的每个gtid都添加到gtid_set中,当文件遍历完成时,这个gtid_set就是@@global.gtid_executed。

        前面说过只有在@@global.gtid_executed为空的情况下,才可以动态设置@@global.gtid_purged。因此可以通过RESET MASTER的方式来清空@@global.gtid_executed。这一点,类似Ares中的命令:set binlog_group_id=XXX, master_server_id=YYY with reset;(是会删除binlog的) 通过解析上面的binlog文件,我们也可以看到,每个事务之前,都有一个GTID_log_event,用来指定GTID的值。总体来看,一个MySQL binlog的格式大致如下:

 

 

 

 我们知道,在未开启GTID模式的情况下,从库用(File_name和File_pos)二元组标识执行到的位置。START SLAVE时,从库会先向主库发送一个BINLOG_DUMP命令,在BINLOG_DUMP命令中指定File_name和File_pos,主库就从这个位置开始发送binlog。

         在开启GTID模式的情况下,如果指定MASTER_AUTO_POSITION=1。START SLAVE时,从库会计算Retrieved_Gtid_Set和Executed_Gtid_Set的并集(通过SHOW SLAVE STATUS可以查看),然后把这个GTID并集发送给主库。主库会使用从库请求的GTID集合和自己的gtid_executed比较,把从库GTID集合里缺失的事务全都发送给从库。如果从库缺失的GTID,已经被主库pruge了呢?从库报1236错误,IO线程中断。

        主库发送的二进制的事件:主库Executed_Gtid_Set对从库(Retrieved_Gtid_Set U Executed_Gtid_Set)的补集

       

 

Retrieved_Gtid_Set:从库已经接收到主库的事务编号
Executed_Gtid_Set:已经执行的事务编号

        考虑下面这种情况,有个集群已经在使用GTID模式同步,想给集群增加一台从库,新做完一台从库,数据和主库一致,但是没有binlog,也就是说新从库的@@global.gtid_executed是空的。但是CHANGE MASTER时可以通过File_name和File_pos找到正确的同步点,然后START SLAVE,一切正常。过了一会儿,觉得还可以通过MASTER_AUTO_POSITION = 1的方式重新CHANGE MASTER,然后再START SLAVE。这种情况下,主库一看从库GTID里少了那么多binlog,然后把全部缺失的binglog再给从库发送一遍,那么悲剧就发生了。

        为了解决这个问题,新做完从库以后,应该在从库上执行reset master; set global gtid_purged = 'xxxxx',把缺失的GTID集合设置为purged,然后就可以直接使用MASTER_AUTO_POSITION=1自动找点儿了

        由此可见,开启GTID以后,Binlog和数据文件一样重要,不仅要求主从数据一致,还要求主从Binlog中GTID集合一致。

 

 

 

 

 

 

 

 

 

posted @ 2019-12-08 17:16  igoodful  阅读(778)  评论(1编辑  收藏  举报