小編給大家分享一下MySQL中server_id一致帶來的問題如何處理,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
我們都知道在MySQL搭建復制環境的時候,需要設置每個server的server_id不一致,如果主庫與從庫的server_id一致,那么復制會失敗。但是最近在解決一個客戶的問題的時候,遇到一個有意思的現象,客戶環境有三臺數據庫服務器,一主兩從,客戶的兩臺從庫設置了相同server_id,在排查問題的過程中,查看MySQL錯誤日志,發現有很多奇怪的信息。
我們模擬了客戶的環境,并進行測試、分析,最終在代碼中找到了我們想要的答案。下面就是我們測試、分析、總結的步驟以及內容。
主庫
IP:192.168.1.130
server_id:3656
從庫A
IP:192.168.1.36
server_id:56
從庫B
IP:192.168.1.57
server_id:56
三臺主機除server_id之外,其余配置如下:
server_id = 123
[client]
socket = /home/mysql/data/mysqldata5.5/sock/mysql.sock
[mysqld]
#server_id = 3655
server_id = 123
port = 3306
skip_name_resolve = 1
binlog_format = ROW
#binlog_format = STATEMENT
basedir = /home/mysql/program/mysql5.5.36
datadir = /home/mysql/data/mysqldata5.5/mydata
socket = /home/mysql/data/mysqldata5.5/sock/mysql.sock
pid-file = /home/mysql/data/mysqldata5.5/sock/mysql.pid
tmpdir = /home/mysql/data/mysqldata5.5/tmpdir
log-error = /home/mysql/data/mysqldata5.5/log/error.log
slow_query_log
slow_query_log_file = /home/mysql/data/mysqldata5.5/slowlog/slow-query.log
log-bin = /home/mysql/data/mysqldata5.5/binlog/mysql-bin
relay-log = /home/mysql/data/mysqldata5.5/relaylog/mysql-relay-bin
innodb_data_home_dir = /home/mysql/data/mysqldata5.5/innodb_ts
innodb_log_group_home_dir = /home/mysql/data/mysqldata5.5/innodb_log
#innodb_undo_directory = /home/mysql/data/mysqldata5.5/undo/
sync_binlog=1
innodb_file_per_table=1
#skip_grant_tables
expire_logs_days = 1
log_slave_updates = ON
#replicate-same-server-id=1
skip_slave_start
#innodb_undo_tablespaces=1
初始搭建環境之后,查看各主機狀態。搭建環境的步驟就省略。
主庫通過show
processlist語句查看,只有一個dump線程,但是通過多次刷新,可以看到連接的是不同的服務器??梢钥吹矫看瓮ㄟ^show
processlist語句顯示的dump線程的Host字段中,IP:PORT的值是不斷在更新的,說明dump線程在不斷的重連,才會出現占用不同的端口的現象。
通過show slave status\G命令查看復制狀態,多次執行可以看到Slave_IO_Running字段顯示的內容,出現YES或者Connnecting兩種狀態??梢钥吹絀/O線程在不斷的進行重連。
并且通過tail -f命令查看error log,可以看到I/O線程一直在嘗試重新連接。
可以看到在錯誤日志中打印的信息是,I/O線程連接
從庫B現象與從庫A一致。
搭建環境步驟省略。
show processlist查看有兩個dump線程,并且多次刷新,發現Host字段中的IP:PORT并沒有修改,說明dump線程一直保持連接。
tail -f /home/mysql/data/mysqldata5.6/log/error.log查看錯誤日志,沒有不斷斷開連接
tail -f /home/mysql/data/mysqldata5.6/log/error.log查看錯誤日志,沒有不斷斷開連接
http://www.penglixun.com/tech/database/mysql_multi_slave_same_serverid.html這是彭立勛寫的關于多個slave使用相同server_id時沖突的原因的一篇文章。按照彭大大的分析,我理解的是,slave的I/O線程連接上主庫的時候,主庫上會調用register_slave()這個函數,在這個函數中又調用了unregister_slave()函數,會將之前使用相同server_id的線程給注銷掉。從而導致從庫的I/O線程不斷斷開重連。
但是仔細看了一下unregister_slave()函數的代碼,并沒有發現MySQL是根據server_id來注銷dump線程的。并且進一步比較了一下5.5.36和5.6.36版本的代碼,并沒有發現不同。而從庫設置server_id一致導致I/O線程不斷重連的現象只在5.5版本中看到,在5.6版本中并沒有這個現象,所以導致5.5現象的原因不是在unregister_slave()函數中。
進一步看了一下彭大大的文章,發現有人在下面評論,說主要是kill_zombie_slave_threads()函數導致的。于是看了一下kill_zombie_slave_threads()函數的邏輯,發現MySQL應該就是在這一步根據server_id將線程kill了。
5.5.36版本
首先來看下5.5.36版本的kill_zombie_dump_threads()函數的代碼??吹竭@個函數傳入的參數是一個uint32類型的slave_server_id,在函數中做的事情是,遍歷MySQL中的所有線程,如果遍歷到一個線程是dump線程并且線程的server_id是等于傳入的參數值話,則跳出遍歷循環,并對kill掉這個線程。
void kill_zombie_dump_threads(uint32 slave_server_id)
{
mysql_mutex_lock(&LOCK_thread_count);
I_List_iterator<THD> it(threads);
THD *tmp;
while ((tmp=it++))
{
if (tmp->command == COM_BINLOG_DUMP &&
tmp->server_id == slave_server_id)
{
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
}
}
mysql_mutex_unlock(&LOCK_thread_count);
if (tmp)
{
/*
Here we do not call kill_one_thread() as
it will be slow because it will iterate through the list
again. We just to do kill the thread ourselves.
*/
tmp->awake(THD::KILL_QUERY);
mysql_mutex_unlock(&tmp->LOCK_thd_data);
}
}
5.6.35版本
再來看一下5.6.36版本的kill_zombie_dump_threads()函數的代碼實現,與5.5.36大不相同。首先傳入的參數是一THD類型的指針,在函數中實現的邏輯同樣是遍歷MySQL中的所有線程,如果找到dump線程,首先看一下這個線程有沒有uuid字段(因為uuid是在5.6之后的版本才有的,這邊是為了兼容5.5),如果有uuid則用uuid進行比較,如果沒有uuid,則用server_id進行比較。
void kill_zombie_dump_threads(THD *thd)
{
String slave_uuid;
get_slave_uuid(thd, &slave_uuid);
if (slave_uuid.length() == 0 && thd->server_id == 0)
return;
mysql_mutex_lock(&LOCK_thread_count);
THD *tmp= NULL;
Thread_iterator it= global_thread_list_begin();
Thread_iterator end= global_thread_list_end();
bool is_zombie_thread= false;
for (; it != end; ++it)
{
if ((*it) != thd && ((*it)->get_command() == COM_BINLOG_DUMP || (*it)->get_command() == COM_BINLOG_DUMP_GTID))
{
String tmp_uuid;
get_slave_uuid((*it), &tmp_uuid);
if (slave_uuid.length())
{
is_zombie_thread= (tmp_uuid.length() && !strncmp(slave_uuid.c_ptr(),
tmp_uuid.c_ptr(), UUID_LENGTH));
else
{
/*
? Check if it is a 5.5 slave's dump thread i.e., server_id should be
? same && dump thread should not contain 'UUID
函數調用
知道了kill_zombie_dump_threads()線程實現的邏輯,那MySQL是在什么地方會調用這個函數的呢??戳艘幌潞瘮凳窃赾ase COM_BINLOG_DUMP中被調用的。
在5.5.36版本中是在
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);
unregister_slave(thd,1,1);
/* fake COM_QUIT -- if we get here, the thread needs to terminate */
error = TRUE;
break;
}
在5.6.36版本中也是在case COM_BINLOG_DUMP中,只不過是將之前的邏輯封裝在了com_binlog_dump()函數中了,kill_zombie_dump_threads()也是在com_binlog_dump()函數中調用的。
case COM_BINLOG_DUMP:
error= com_binlog_dump(thd, packet, packet_length);
break
case COM_BINLOG_DUMP中所進行的操作就是將dump線程通知I/O線程拉取新的binlog。
看完了這篇文章,相信你對“MySQL中server_id一致帶來的問題如何處理”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。