美文网首页
深入理解MySQL 5.7 GTID系列(三):GTID的生成时

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

作者: 暖夏未眠丶 | 来源:发表于2018-01-16 16:31 被阅读62次

    摘要: 一、GTID生成类型这里首先使用源码的解释给出三种类型: AUTOMATIC_GROUP GTID_GROUP ANONYMOUS_GROUP 其中AUTOMATIC_GROUP通常用于主库开启GTID的情况,GTID_GROUP通常用于备库和使用了GTID_NEXT的情况下。

    一、GTID生成类型

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

    AUTOMATIC_GROUP

    GTID_GROUP

    ANONYMOUS_GROUP

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

    源码中有详细解释如下:

    /**

        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

    二、GTID和LAST_COMMT/SEQUNCE_NUMBER的生成时机

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

    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中生成GTID和LAST_COMMT/SEQUNCE_NUMBER的代码片段:

    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_committedint64 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();

    其调用栈帧如下:

    #0Gtid_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#10x0000000001803248inGtid_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#20x0000000001845703inMYSQL_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#30x0000000001846307inbinlog_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#40x0000000001860e57inbinlog_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#50x0000000001856d46inMYSQL_BIN_LOG::flush_thread_caches (this=0x2dffc80, thd=0x7fff2c000b70) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/binlog.cc:8470#60x0000000001856f77inMYSQL_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#70x0000000001858593inMYSQL_BIN_LOG::ordered_commit (this=0x2dffc80, thd=0x7fff2c000b70, all=false, skip_commit=false)

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

    三、GTID_STATE::GENERATE_AUTOMATIC_GTID逻辑

    // If GTID_MODE = ON_PERMISSIVE or ON, generate a new GTIDif(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的sidnolock_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 gnoif(automatic_gtid.gno !=-1)    acquire_ownership(thd, automatic_gtid);//此处将这个gtid 及上面的SIDNO:gno加入到owned_gtids中 并且赋予给线程 经过本步骤 可以显示elseret= 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逻辑while(true) {constGtid_set::Interval *iv= ivit.get();//定义Interval指针指向 这个链表指针开头,如果在进行下次循环会获得NULLrpl_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 则指向了本区间最大的值+1ivit.next(); }

    五、本节小结

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

    1、GTID在主库什么时候时候生成。

    2、LAST_COMMIT/SEQUENCE_NUMBER什么时候生成。

    3、GTID的生成逻辑是怎么样的。

    原文发布时间为:2018-01-13

    本文作者:高鹏(重庆八怪)

    本文来自云栖社区合作伙伴“老叶茶馆”,了解相关信息可以关注“老叶茶馆”微信公众号

    如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:yqgroup@service.aliyun.com 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容

    原文链接

    相关文章

      网友评论

          本文标题:深入理解MySQL 5.7 GTID系列(三):GTID的生成时

          本文链接:https://www.haomeiwen.com/subject/pfzsoxtx.html