本文內(nèi)容遵從CC版權(quán)協(xié)議, 可以隨意轉(zhuǎn)載, 但必須以超鏈接形式標明文章原始出處和作者信息及版權(quán)聲明網(wǎng)址: http://www.penglixun.com/tech/database/mysql_multi_slave_same_serverid.html 今天分析一個詭異問題,一個模擬Slave線程的程序,不斷的被Master Ser
本文內(nèi)容遵從CC版權(quán)協(xié)議, 可以隨意轉(zhuǎn)載, 但必須以超鏈接形式標明文章原始出處和作者信息及版權(quán)聲明網(wǎng)址: http://www.penglixun.com/tech/database/mysql_multi_slave_same_serverid.html
今天分析一個詭異問題,一個模擬Slave線程的程序,不斷的被Master Server給kill掉,最終發(fā)現(xiàn)是因為有兩個Slave使用同樣一個server id去連接Master Server,為什么兩個Slave用同一個server id會被Master Server給Kill呢?分析了源碼,這源于MySQL Replication的重連機制。
我們首先看看一個Slave注冊到Master會發(fā)生什么,首先Slave需要向Master發(fā)送一個COM_REGISTER_SLAVE類型的請求(sql_parse.cc)命令請求,這里Master會使用register_slave函數(shù)注冊一個Slave到slave_list。
case COM_REGISTER_SLAVE: { if (!register_slave(thd, (uchar*)packet, packet_length)) my_ok(thd); break; }
在注冊Slave線程的時候會發(fā)生什么呢?我們略去無用的代碼直接看重點:(repl_failsafe.cc)
int register_slave(THD* thd, uchar* packet, uint packet_length) { int res; SLAVE_INFO *si; uchar *p= packet, *p_end= packet + packet_length; .... //省略 if (!(si->master_id= uint4korr(p))) si->master_id= server_id; si->thd= thd; pthread_mutex_lock(&LOCK_slave_list); unregister_slave(thd,0,0); //關(guān)鍵在這里,先取消注冊server_id相同的Slave線程 res= my_hash_insert(&slave_list, (uchar*) si); //把新的Slave線程注冊到slave_list pthread_mutex_unlock(&LOCK_slave_list); return res; ..... }
這是什么意思呢?這就是重連機制,slave_list是一個Hash表,server_id是Key,每一個線程注冊上來,需要刪掉同樣server_id的Slave線程,再把新的Slave線程加到slave_list表中。
線程注冊上來后,請求Binlog,發(fā)送COM_BINLOG_DUMP請求,Master會發(fā)送binlog給Slave,代碼如下:
case COM_BINLOG_DUMP: { ulong pos; ushort flags; uint32 slave_server_id; status_var_increment(thd->status_var.com_other); thd->enable_slow_log= opt_log_slow_admin_statements; if (check_global_access(thd, REPL_SLAVE_ACL)) break; /* TODO: The following has to be changed to an 8 byte integer */ pos = uint4korr(packet); flags = uint2korr(packet + 4); thd->server_id=0; /* avoid suicide */ if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0 kill_zombie_dump_threads(slave_server_id); thd->server_id = slave_server_id; general_log_print(thd, command, "Log: '%s' Pos: %ld", packet+10, (long) pos); mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags); //不斷的發(fā)送日志給slave端 unregister_slave(thd,1,1); //發(fā)送完成后清理Slave線程,因為執(zhí)行到這一步肯定是binlog dump線程被kill了 /* fake COM_QUIT -- if we get here, the thread needs to terminate */ error = TRUE; break; }
mysql_binlog_send函數(shù)在sql_repl.cc,里面是輪詢Master binlog,發(fā)送給Slave。
再來簡單看看unregister_slave做了什么(repl_failsafe.cc):
void unregister_slave(THD* thd, bool only_mine, bool need_mutex) { if (thd->server_id) { if (need_mutex) pthread_mutex_lock(&LOCK_slave_list); SLAVE_INFO* old_si; if ((old_si = (SLAVE_INFO*)hash_search(&slave_list, (uchar*)&thd->server_id, 4)) && (!only_mine || old_si->thd == thd)) //拿到slave值 hash_delete(&slave_list, (uchar*)old_si); //從slave_list中拿掉 if (need_mutex) pthread_mutex_unlock(&LOCK_slave_list); } }
這就可以解釋同樣的server_id為什么會被kill,因為一旦注冊上去,就會現(xiàn)刪除相同server_id的Slave線程,然后把當(dāng)前的Slave加入,這是因為有時Slave斷開了,重新請求上來,當(dāng)然需要踢掉原來的線程,這就是線程重連機制。
切記,一個MySQL集群中,絕不可以出現(xiàn)相同server_id的實例,否則各種詭異的問題可是接踵而來。
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com