一、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_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();
複制
其調用棧幀如下:
#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_GTID和GTID_STATE::GET_AUTOMATIC_GNO邏輯,他們用于生成一個GTID。
三、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();//釋放讀寫鎖
複制
接下來看看GNO的生成邏輯GTID_STATE::GET_AUTOMATIC_GNO。
四、GTID_STATE::GENERATE_AUTOMATIC_GTID邏輯
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的生成邏輯是怎麼樣的。
如果有源碼閱讀能力的同學可以按照這個架構繼續深入學習。