問題描述:
結構的1主2從,機器名分別為M1、S1、S2。使用keepalived+haproxy,將M1、S1做心跳,在M1正常的情況下,S1只負責讀,如果M1掛了,則讓S1變成讀+寫。
問題如下:在M1掛掉,S1升級成新主機的情況下,S2如何能夠自動將主機變更為S1,並且知道S1接手以後,新的日志文件的名稱和position。
最佳回答:
備庫如何發起DUMP請求
引入GTID,最大的好處當然是我們可以隨心所欲的切換主備拓撲結構了。在一個正常運行的復制結構中,我們可以在備庫簡單的執行如下SQL:
CHANGE MASTER TO MASTER_USER=’$USERNAME’, MASTER_HOST=’ ‘, MASTER_PORT=’ ‘, MASTER_AUTO_POSITION=1;
打開GTID後,我們就無需指定binlog文件或者位置,MySQL會自動為我們做這些事情。這裡的關鍵就是MASTER_AUTO_POSITION。IO線程連接主庫,可以大概分為以下幾步:
1.IO線程在和主庫建立TCP鏈接後,會去獲取主庫的uuid(get_master_uuid),然後在主庫上設置一個用戶變量@slave_uuid(io_thread_init_commands)
2.之後,在主庫上注冊SLAVE(register_slave_on_master)
在主庫上調用register_slave來注冊備庫,將備庫的host,user,password,port,server_id等信息記錄到slave_list哈希中。
3.調用request_dump,開始向主庫請求數據,這裡分兩種情況:
MASTER_AUTO_POSITION=0時,向主庫發送命令的類型為COM_BINLOG_DUMP,這是傳統的請求BINLOG的模式
MASTER_AUTO_POSITION=1時,命令類型為COM_BINLOG_DUMP_GTID,這是新的方式。
這裡我們只討論第二種。第二種情況下,會先去讀取備庫已經執行的gtid集合
quoted code in rpl_slave.cc :
if (command == COM_BINLOG_DUMP_GTID)
{
// get set of GTIDs
Sid_map sid_map(NULL/*no lock needed*/);
Gtid_set gtid_executed(&sid_map);
global_sid_lock->wrlock();
gtid_state->dbug_print();
if (gtid_executed.add_gtid_set(mi->rli->get_gtid_set()) != RETURN_STATUS_OK ||
gtid_executed.add_gtid_set(gtid_state->get_logged_gtids()) !=
RETURN_STATUS_OK)
構建完成發送包後,發送給主庫。
在主庫上接受到命令後,調用入口函數com_binlog_dump_gtid,流程如下:
1.slave_gtid_executed.add_gtid_encoding(packet_position, data_size) ;讀取備庫傳來的GTID SET
2.讀取備庫的uuid(get_slave_uuid),被根據uuid來kill僵屍線程(kill_zombie_dump_threads)
這也是之前SLAVE IO線程執行SET @SLAVE_UUID的用處。
3.進入mysql_binlog_send函數:
|?>調用MYSQL_BIN_LOG::find_first_log_not_in_gtid_set,從最後一個Binlog開始掃描,獲取文件頭部的PREVIOUS_GTIDS_LOG_EVENT,如果它是slave_gtid_executed的子集,保存當前binlog文件名,否則繼續向前掃描。
這一步的目的就是為了找出備庫執行到的最後一個Binlog文件。
|?>從這個文件頭部開始掃描,遇到GTID_EVENT時,會去判斷該GTID是否包含在slave_gtid_executed中:
Gtid_log_event gtid_ev(packet->ptr() + ev_offset,
packet->length() ? checksum_size,
p_fdle);
skip_group= slave_gtid_executed->contains_gtid(gtid_ev.get_sidno(sid_map),
gtid_ev.get_gno());
主庫通過GTID決定是否可以忽略事務,從而決定執行開始的位置
注意,在使用MASTER_LOG_POSITION後,就不要指定binlog的位置,否則會報錯。