読者です 読者をやめる 読者になる 読者になる

slaveからダンプをとった場合のCHANGE MASTER TOについて

MySQLのslaveサーバからcronで一日一回ダンプファイルを取得してて、
そのダンプファイルを使ってどこかのサーバにリストアした場合、
新しくレプリケーションを開始する際に発行するCHANGE MASTER TOコマンドは一体何を指定したらいいんだろう?


例えばmasterサーバでダンプを取得する場合、mysqldumpコマンドのオプションとして
「--master-data=2」を指定してやると、dumpファイル中にCHANGE MASTERコマンドが付加される。
これは非常に便利なんだけど、あくまでshow master statusの値を取っているので
slaveサーバでこれつけてもまったく意味がない。


slaveでダンプとるときも、こんな便利オプションないかなーと思ってたんですが、
MySQL 5.5から--dump-slaveってのが追加されるようです。

--dump-slave[=value]

This option is similar to --master-data except that it is used to dump a replication slave server to produce a dump file that can be used to set up another server as a slave that has the same master as the dumped server. It causes the dump output to include a CHANGE MASTER TO statement that indicates the binary log coordinates (file name and position) of the dumped slave's master (rather than the coordinates of the dumped server, as is done by the --master-data option). These are the master server coordinates from which the slave should start replicating. This option was added in MySQL 5.5.3.

http://dev.mysql.com/doc/refman/5.5/en/mysqldump.html#option_mysqldump_dump-slave

MySQL 5.5もいつの間にかrc版になっていて、リリースも近いようですが、
今現在つかってるMySQL5.1系の場合でも同じようなことがやりたい!
ってことで--dump-slaveオプションが何をやってるか少し調べてみました。

mysqldump.c

MySQL-5.5.6-rcをダウンロード/展開してmysqldump.cを見てみます。(関係ありそうなとこだけ)

./mysql-5.5.6-rc/client/mysqldump.c

  {"dump-slave", OPT_MYSQLDUMP_SLAVE_DATA,
   "This causes the binary log position and filename of the master to be "
   "appended to the dumped data output. Setting the value to 1, will print"
   "it as a CHANGE MASTER command in the dumped data output; if equal"
   " to 2, that command will be prefixed with a comment symbol. "
   "This option will turn --lock-all-tables on, unless "
   "--single-transaction is specified too (in which case a "
   "global read lock is only taken a short time at the beginning of the dump "
   "- don't forget to read about --single-transaction below). In all cases "
   "any action on logs will happen at the exact moment of the dump."
   "Option automatically turns --lock-tables off.",
   &opt_slave_data, &opt_slave_data, 0,
   GET_UINT, OPT_ARG, 0, 0, MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL, 0, 0, 0},

まずここ。
んで主な処理は↓の3つですね。

 if (opt_slave_data && do_stop_slave_sql(mysql))
   goto err;
  if (opt_slave_data && do_show_slave_status(mysql))
    goto err;
  if (opt_slave_data && do_start_slave_sql(mysql))
    goto err;

名前からしてわかるけど、主な流れとしては
stop_slave → show_slave_status → start_slave
みたいですね。 詳細は↓

do_stop_slave_sql
static int do_stop_slave_sql(MYSQL *mysql_con)
{
  (省略)
  /* now, stop slave if running */
  if (mysql_query_with_error_report(mysql_con, 0, "STOP SLAVE SQL_THREAD"))
    return(1);
  return(0);
}

でまずは"STOP SLAVE SQL_THREAD"の発行。
うお!STOP SLAVEじゃなくてSTOP SLAVE SQL_THREADなのか。。
たしかにSQL_THREADだけ止めとけば、SQLは実行されないからbinログのポジションは変動しませんね。

do_show_slave_status

でここがきもなんだけど

static int do_show_slave_status(MYSQL *mysql_con)
{
  MYSQL_RES *slave;
  const char *comment_prefix=
    (opt_slave_data == MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL) ? "-- " : "";
  if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS"))
  {
    if (!ignore_errors)
    {
      /* SHOW SLAVE STATUS reports nothing and --force is not enabled */
      my_printf_error(0, "Error: Slave not set up", MYF(0));
    }
    mysql_free_result(slave);
    return 1;
  }
  else
  {
    MYSQL_ROW row= mysql_fetch_row(slave);
    if (row && row[9] && row[21])
    {
      /* SHOW MASTER STATUS reports file and position */      if (opt_comments)
        fprintf(md_result_file,
                "\n--\n-- Position to start replication or point-in-time "
                "recovery from (the master of this slave)\n--\n\n");

      fprintf(md_result_file, "%sCHANGE MASTER TO ", comment_prefix);

      if (opt_include_master_host_port)
      {
        if (row[1])
          fprintf(md_result_file, "MASTER_HOST='%s', ", row[1]);
        if (row[3])
          fprintf(md_result_file, "MASTER_PORT='%s', ", row[3]);
      }
      fprintf(md_result_file,
              "MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", row[9], row[21]);

      check_io(md_result_file);
    }
    mysql_free_result(slave);
  }
  return 0;
}

ぐ…省略できるところがない。。
えーと上のほうでまず"SHOW SLAVE STATUS"を発行して…

      fprintf(md_result_file,
              "MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", row[9], row[21]);

      check_io(md_result_file);

↑ここかな。
show slave statusの結果を配列にいれて、
row[9]とrow[21]をそれぞれMASTER_LOG_FILEとMASTER_LOG_POSとすると。
長いけどshow slave statusの結果はこんな感じだから…

mysql> show slave status \G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.1.XX
                  Master_User: foo
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: bin-log.000001
          Read_Master_Log_Pos: 1000000
               Relay_Log_File: relay-log.0000001
                Relay_Log_Pos: 1000000
        Relay_Master_Log_File: bin-log.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 1000000
              Relay_Log_Space: 1000000
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
1 row in set (0.00 sec)

mysql> 

配列の9番目と21番目だからー
MASTER_LOG_FILE が Relay_Master_Log_File の bin-log.000001
MASTER_LOG_POS が Exec_Master_Log_Pos の 1000000
だね。

do_start_slave_sql
static int do_start_slave_sql(MYSQL *mysql_con)
{
  (省略)
  /* now, start slave if stopped */
  if (mysql_query_with_error_report(mysql_con, 0, "START SLAVE"))
  {
    my_printf_error(0, "Error: Unable to start slave", MYF(0));
    return 1;
  }
  return(0);
}

で、最後に"START SLAVE"を実行してやると。

まとめると。

slaveからダンプをとった場合のCHANGE MASTER TOは

  1. STOP SLAVE SQL_THREAD
  2. SHOW SLAVE STATUS
    • Relay_Master_Log_FileとExec_Master_Log_Posの値を取る
  3. mysqldump
  4. START SLAVE

としてやったときの値になると。
うん、これぐらいならperlでスクリプト書けそうだな。
MySQL 5.5がでたら--dump-slaveオプションを使おう。