一,GTID 生成类型
这里首先使用源码的解释给出三种类型:
其中 AUTOMATIC_GROUP 通常用于主库开启 GTID 的情况,GTID_GROUP 通常用于备库和使用了 GTID_NEXT 的情况下.
AUTOMATIC_GROUP
GTID_GROUP
ANONYMOUS_GROUP
源码中有详细解释如下:
二,GTID 和 LAST_COMMT/SEQUNCE_NUMBER 的生成时机
/**
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 其实是在 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 函数的片段:
下面是 MySQL_BIN_LOG.WRITE_GTID 中生成 GTID 和 LAST_COMMT/SEQUNCE_NUMBER 的代码片段:
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写入到文件
其调用栈帧如下:
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();
接下来我们就需要具体研究下一个 GTID 是依靠什么逻辑生成的.我们需要查看函数 GTID_STATE::GENERATE_AUTOMATIC_GTID 和 GTID_STATE::GET_AUTOMATIC_GNO 逻辑,他们用于生成一个 GTID.
#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_STATE::GENERATE_AUTOMATIC_GTID 逻辑
接下来看看GNO的生成逻辑GTID_STATE: :GET_AUTOMATIC_GNO.四,GTID_STATE: :GENERATE_AUTOMATIC_GTID逻辑
// 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(); //释放读写锁
五,本节小结
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-13
来源: https://yq.aliyun.com/articles/364921