萬盛學電腦網

 萬盛學電腦網 >> 數據庫 >> mysql教程 >> MySQL Slave同一server_id的沖突原因分析

MySQL Slave同一server_id的沖突原因分析

今天分析一個詭異問題,一個模擬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斷開了,重新請求上來,當然需要踢掉原來的線程,這就是線程重連機制。

copyright © 萬盛學電腦網 all rights reserved