前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解MySQL 5.7 GTID系列(三):GTID的生成时机

深入理解MySQL 5.7 GTID系列(三):GTID的生成时机

作者头像
wubx
发布2019-04-24 18:41:25
9200
发布2019-04-24 18:41:25
举报
文章被收录于专栏:MySQLBeginnerMySQLBeginner

作者:高鹏(重庆八怪) 原文地址: https://www.jianshu.com/p/6ee969dc2c9b 深入理解MySQL 5.7 GTID系列文章共十篇,本文为第三篇, 第一篇:深入理解MySQL 5.7 GTID系列(一) 第二篇:深入理解MySQL 5.7 GTID系列(二):GTID相关内部数据结构 该系列文章将陆续不定期更新~

一、GTID生成类型

这里首先使用源码的解释给出三种类型:

  • AUTOMATIC_GROUP
  • GTID_GROUP
  • ANONYMOUS_GROUP

其中AUTOMATIC_GROUP通常用于主库开启GTID的情况,GTID_GROUP通常用于备库和使用了GTID_NEXT的情况下。

源码中有详细解释如下:

代码语言:javascript
复制
  /**
   Specifies that the GTID has not been generated yet; it will be
   generated on commit.  It will depend on the GTID_MODE: if
   GTID_MODE<=OFF_PERMISSIVE, then the transaction will be anonymous;
   if GTID_MODE>=ON_PERMISSIVE, then the transaction will be assigned
   a new GTID.   This is the default value: thd->variables.gtid_next has this state
   when GTID_NEXT="AUTOMATIC".   It is important that AUTOMATIC_GROUP==0 so that the default value
   for thd->variables->gtid_next.type is AUTOMATIC_GROUP.
 */
 AUTOMATIC_GROUP= 0,
 /**
   Specifies that the transaction has been assigned a GTID (UUID:NUMBER).   thd->variables.gtid_next has this state when GTID_NEXT="UUID:NUMBER".   This is the state of GTID-transactions replicated to the slave.
 */
 GTID_GROUP,
 /**
   Specifies that the transaction is anonymous, i.e., it does not
   have a GTID and will never be assigned one.   thd->variables.gtid_next has this state when GTID_NEXT="ANONYMOUS".   This is the state of any transaction generated on a pre-GTID
   server, or on a server with GTID_MODE==OFF.
 */
ANONYMOUS_GROUP

二、GTIDLAST_COMMT/SEQUNCE_NUMBER的生成时机

GTID其实是在COMMIT的时候调用MySQL_BIN_LOG::ORDERED_COMMIT执行到FLUSH阶段产生GTID EVENT的时候才生成,生成后会将这个GTID加入到GTID_STATEOWNED_GTIDS中,实际上这个过程不仅要生成GTID还会生成SEQUENCE_NUMBERLAST_COMMIT并且会构造GTID_EVENT写入到BINLOG CACHE,最后将BINLOG CACHE写入到BINLOG FILE(注意这里还没有调用FSYNC真正落盘),下面是BINLOG_CACHE_DATA::FLUSH函数的片段:

代码语言:javascript
复制
if (!error)
     if ((error= mysql_bin_log.write_gtid(thd, this, &writer))) //生成Gtid和Last_commt/sequnce_number构造好Gtid event并且写入到到binlog cache中
       thd->commit_error= THD::CE_FLUSH_ERROR;
if (!error)
     error= mysql_bin_log.write_cache(thd, this, &writer);  //将binlog cache写入到文件

下面是MySQL_BIN_LOG.WRITE_GTID中生成GTIDLAST_COMMT/SEQUNCE_NUMBER的代码片段:

代码语言:javascript
复制
if (thd->variables.gtid_next.type == AUTOMATIC_GROUP)//如果过是非指定的Gtid则需要自动生成调用generate_automatic_gtid生成
 {
   if (gtid_state->generate_automatic_gtid(thd,
           thd->get_transaction()->get_rpl_transaction_ctx()->get_sidno(),
           thd->get_transaction()->get_rpl_transaction_ctx()->get_gno())
           != RETURN_STATUS_OK)
     DBUG_RETURN(true);
 }
.....
//下面生成sequence_number和last_committed
 int64 relative_sequence_number= trn_ctx->sequence_number - clock.get_offset();
 int64 relative_last_committed=
   trn_ctx->last_committed <= clock.get_offset() ?
   SEQ_UNINIT : trn_ctx->last_committed - clock.get_offset();

其调用栈帧如下:

代码语言:javascript
复制
#0  Gtid_state::get_automatic_gno (this=0x2ff8bb0, sidno=1) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/rpl_gtid_state.cc:564
#1  0x0000000001803248 in Gtid_state::generate_automatic_gtid (this=0x2ff8bb0, thd=0x7fff2c000b70, specified_sidno=0, specified_gno=0)
   at /root/mysql5.7.14/percona-server-5.7.14-7/sql/rpl_gtid_state.cc:628
#2  0x0000000001845703 in MYSQL_BIN_LOG::write_gtid (this=0x2dffc80, thd=0x7fff2c000b70, cache_data=0x7fff2c021178, writer=0x7ffff0358810)
   at /root/mysql5.7.14/percona-server-5.7.14-7/sql/binlog.cc:1167
#3  0x0000000001846307 in binlog_cache_data::flush (this=0x7fff2c021178, thd=0x7fff2c000b70, bytes_written=0x7ffff03588b8, wrote_xid=0x7ffff0358917)
   at /root/mysql5.7.14/percona-server-5.7.14-7/sql/binlog.cc:1454
#4  0x0000000001860e57 in binlog_cache_mngr::flush (this=0x7fff2c020ff0, thd=0x7fff2c000b70, bytes_written=0x7ffff0358918, wrote_xid=0x7ffff0358917)
   at /root/mysql5.7.14/percona-server-5.7.14-7/sql/binlog.cc:768
#5  0x0000000001856d46 in MYSQL_BIN_LOG::flush_thread_caches (this=0x2dffc80, thd=0x7fff2c000b70) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/binlog.cc:8470
#6  0x0000000001856f77 in MYSQL_BIN_LOG::process_flush_stage_queue (this=0x2dffc80, total_bytes_var=0x7ffff0358a88, rotate_var=0x7ffff0358a87,
   out_queue_var=0x7ffff0358a78) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/binlog.cc:8532
#7  0x0000000001858593 in MYSQL_BIN_LOG::ordered_commit (this=0x2dffc80, thd=0x7fff2c000b70, all=false, skip_commit=false)

接下来我们就需要具体研究下一个GTID是依靠什么逻辑生成的。我们需要查看函数GTID_STATE::GENERATE_AUTOMATIC_GTIDGTID_STATE::GET_AUTOMATIC_GNO逻辑,他们用于生成一个GTID

三、GTID_STATE::GENERATE_AUTOMATIC_GTID逻辑

代码语言:javascript
复制
 // If GTID_MODE = ON_PERMISSIVE or ON, generate a new GTID
 if (get_gtid_mode(GTID_MODE_LOCK_SID) >= GTID_MODE_ON_PERMISSIVE)//如果GTID_MODE是ON_PERMISSIVE和ON则生成GTID
 {
   Gtid automatic_gtid= { specified_sidno, specified_gno };   if (automatic_gtid.sidno == 0)//如果是备库则sidno>0,如果是主库sidno==0,因为主库的Gtid这个时候才生成,但是备库则是使用GTID_GROUP指定生成
     automatic_gtid.sidno= get_server_sidno();//此处返回本server的sidno   lock_sidno(automatic_gtid.sidno);//此处对并发生成GNO的多个线程进行控制   if (automatic_gtid.gno == 0)//如果是备库则gno>0,如果是主库gno == 0,因为主库的Gtid这个时候才生成,但是备库则是使用GTID_GROUP指定生成
     automatic_gtid.gno= get_automatic_gno(automatic_gtid.sidno);//此处返回最后指定sidno的end gno   if (automatic_gtid.gno != -1)
     acquire_ownership(thd, automatic_gtid);//此处将这个gtid 及上面的SIDNO:gno加入到owned_gtids中 并且赋予给线程 经过本步骤 可以显示
   else
     ret= RETURN_STATUS_REPORTED_ERROR;   unlock_sidno(automatic_gtid.sidno);//分配完成其他线程可以分配
 }
 else //如果是OFF_PERMISSIVE或者OFF状态如何处理 这里不做讨论了
 {
   // If GTID_MODE = OFF or OFF_PERMISSIVE, just mark this thread as
   // using an anonymous transaction.
   thd->owned_gtid.sidno= THD::OWNED_SIDNO_ANONYMOUS;
   thd->owned_gtid.gno= 0;
   acquire_anonymous_ownership();
   thd->owned_gtid.dbug_print(NULL,
                              "set owned_gtid (anonymous) in generate_automatic_gtid");
 } sid_lock->unlock();//释放读写锁

接下来看看GNO的生成逻辑GTID_STATE::GET_AUTOMATIC_GNO

四、GTID_STATE::GENERATE_AUTOMATIC_GTID逻辑

代码语言:javascript
复制
while (true)
 {
   const Gtid_set::Interval *iv= ivit.get(); //定义Interval指针指向 这个链表指针开头,如果在进行下次循环会获得NULL
   rpl_gno next_interval_start= iv != NULL ? iv->start : MAX_GNO; //正常情况下不会为NULL因此 next_interval_start 等于第一个interval的start,当然如果初始化会为NULL,
                                                                  //如果Interval->next =NULL 则标示没有区间了。
   while (next_candidate.gno < next_interval_start &&
          DBUG_EVALUATE_IF("simulate_gno_exhausted", false, true)) //这里next_candidate.gno正常不会小于next_interval_start ,如果Interval->next =NULL或者初始化
                                                                   //next_interval_start会被制为MAX_GNO那么条件成立  
                                                                   //DBUG_RETURN(next_candidate.gno);返回了这个gno 则GTID生成
   {
     if (owned_gtids.get_owner(next_candidate) == 0) //如果本GTID已经被其他线程占用则next_candidate.gno++;返回这个gno。
       DBUG_RETURN(next_candidate.gno);
     next_candidate.gno++;
   }
   if (iv == NULL ||
       DBUG_EVALUATE_IF("simulate_gno_exhausted", true, false))
   {
     my_error(ER_GNO_EXHAUSTED, MYF(0));
     DBUG_RETURN(-1);
   }
   next_candidate.gno= iv->end; //iv->end 则指向了本区间最大的值+1
   ivit.next();
 }

五、本节小结

学习完本节至少能够学习到:

  • 1、GTID在主库什么时候时候生成。
  • 2、LAST_COMMIT/SEQUENCE_NUMBER什么时候生成。
  • 3、GTID的生成逻辑是怎么样的。

如果有源码阅读能力的同学可以按照这个框架继续深入学习。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-01-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 MySQLBeginner 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、GTID生成类型
  • 二、GTID和LAST_COMMT/SEQUNCE_NUMBER的生成时机
  • 三、GTID_STATE::GENERATE_AUTOMATIC_GTID逻辑
  • 四、GTID_STATE::GENERATE_AUTOMATIC_GTID逻辑
  • 五、本节小结
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档