视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
MySQL多个Slave同一server_id的冲突原因分析
2020-11-09 13:19:25 责编:小采
文档

本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/database/mysql_multi_slave_same_serverid.html 今天分析一个诡异问题,一个模拟Slave线程的程序,不断的被Master Ser

本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/database/mysql_multi_slave_same_serverid.html

今天分析一个诡异问题,一个模拟Slave线程的程序,不断的被Master Server给kill掉,最终发现是因为有两个Slave使用同样一个server id去连接Master Server,为什么两个Slave用同一个server id会被Master Server给Kill呢?分析了源码,这源于MySQL Replication的重连机制。

我们首先看看一个Slave注册到Master会发生什么,首先Slave需要向Master发送一个COM_REGISTER_SLAVE类型的请求(sql_parse.cc)命令请求,这里Master会使用register_slave函数注册一个Slave到slave_list。

 case COM_REGISTER_SLAVE:
 {
 if (!register_slave(thd, (uchar*)packet, packet_length))
 my_ok(thd);
 break;
 }

在注册Slave线程的时候会发生什么呢?我们略去无用的代码直接看重点:(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); //关键在这里,先取消注册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,发送COM_BINLOG_DUMP请求,Master会发送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); //不断的发送日志给slave端
 unregister_slave(thd,1,1); //发送完成后清理Slave线程,因为执行到这一步肯定是binlog dump线程被kill了
 /* fake COM_QUIT -- if we get here, the thread needs to terminate */
 error = TRUE;
 break;
 }

mysql_binlog_send函数在sql_repl.cc,里面是轮询Master binlog,发送给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,因为一旦注册上去,就会现删除相同server_id的Slave线程,然后把当前的Slave加入,这是因为有时Slave断开了,重新请求上来,当然需要踢掉原来的线程,这就是线程重连机制。

切记,一个MySQL集群中,绝不可以出现相同server_id的实例,否则各种诡异的问题可是接踵而来。

下载本文
显示全文
专题