gtidを使用した環境でのmysqldump

以前の日記Global Transaction IDを使ってレプリケーション構成とした際の制限でmysqldumpから新規にスレーブを作ろうとするとgtidが記録されなくてバイナリログ使わないとダメ、と書いたんだけど、5.6.10で試したら普通にスレーブを作成可能だったので訂正も含めて書きます。

よくあるmysqldump

MySQL5.5系まででgtidが存在しない環境では良く以下のようにmysqldumpを実行しているかと思います(InnoDBのみ利用の場合)。

# mysqldump --all-databases --single-transaction --master-data=2 > /path/to/dir/dump
または
# mysqldump --all-databases --single-transaction --master-data=2 | gzip > /path/to/dir/dump.gz
イベントスケジューラーとかその他色々使ってる場合
# mysqldump --all-databases --single-transaction --master-data=2 --triggers --routines --events > /path/to/dir/dump

なお、gtidを利用した環境では--triggers --routines --eventsオプションを使用しない場合は以下のようにWarningが出力されます。

# mysqldump --all-databases --single-transaction --master-data=2 > /tmp/dump
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database. If you don't want to restore GTIDs, pass --set-gtid-purged=OFF. To make a complete dump, pass --all-databases --triggers --routines --events. 
#

gtid環境でmysqldumpを取得した場合のダンプファイル

上記のmysqldump実行例の以下内容でダンプを取得します。

# mysqldump --all-databases --single-transaction --master-data=2 --triggers --routines --events > /path/to/dir/dump

ダンプファイルの中身を覗いてみます。

# mysqldump --all-databases --single-transaction --master-data=2 --triggers --routines --events > /tmp/dump
# head -30 /tmp/dump 
-- MySQL dump 10.13  Distrib 5.6.10, for Linux (x86_64)
--
-- Host: localhost    Database: 
-- ------------------------------------------------------
-- Server version	5.6.10-log

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
SET @@SESSION.SQL_LOG_BIN= 0;

--
-- GTID state at the beginning of the backup 
--

SET @@GLOBAL.GTID_PURGED='71803be2-7816-11e2-8c99-5254003e1cec:1-665';

--
-- Position to start replication or point-in-time recovery from
--

-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000006', MASTER_LOG_POS=197930;

ここで見慣れない以下内容が記録されています。

SET @@GLOBAL.GTID_PURGED='71803be2-7816-11e2-8c99-5254003e1cec:1-665';

gtid関連のパラメータとして、MySQL5.6.10では以下パラメータが存在しています。以下はMaster側のダンプを取得したMySQLの内容です。

mysql> show global variables like '%gtid%';
+--------------------------+--------------------------------------------+
| Variable_name            | Value                                      |
+--------------------------+--------------------------------------------+
| enforce_gtid_consistency | ON                                         |
| gtid_executed            | 71803be2-7816-11e2-8c99-5254003e1cec:1-665 |
| gtid_mode                | ON                                         |
| gtid_owned               |                                            |
| gtid_purged              |                                            |
+--------------------------+--------------------------------------------+
5 rows in set (0.00 sec)

mysql> show variables like '%gtid%';
+--------------------------+-----------+
| Variable_name            | Value     |
+--------------------------+-----------+
| enforce_gtid_consistency | ON        |
| gtid_executed            |           |
| gtid_mode                | ON        |
| gtid_next                | AUTOMATIC |
| gtid_owned               |           |
| gtid_purged              |           |
+--------------------------+-----------+
6 rows in set (0.00 sec)

mysql> 

global無しを書いたのはgtid_nextはsessionのみのパラメータのためです。

リストアしてみる

今回ポイントとなるのはgtid_executedとgtid_purgedの2つ。先に記載のダンプファイルの内容から、リストアした場合はgtid_purgedに値が設定されることが分かります。実際に空の、新しく立ち上げたMySQLへリストアを行います。

まずはリストア前のgtid関連のパラメータの状態

mysql> show global variables like '%gtid%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| enforce_gtid_consistency | ON    |
| gtid_executed            |       |
| gtid_mode                | ON    |
| gtid_owned               |       |
| gtid_purged              |       |
+--------------------------+-------+
5 rows in set (0.00 sec)

mysql> 

リストアを行って確認します。

# mysql < /tmp/dump
# mysql
mysql> show global variables like '%gtid%';
+--------------------------+--------------------------------------------+
| Variable_name            | Value                                      |
+--------------------------+--------------------------------------------+
| enforce_gtid_consistency | ON                                         |
| gtid_executed            | 71803be2-7816-11e2-8c99-5254003e1cec:1-665 |
| gtid_mode                | ON                                         |
| gtid_owned               |                                            |
| gtid_purged              | 71803be2-7816-11e2-8c99-5254003e1cec:1-665 |
+--------------------------+--------------------------------------------+
5 rows in set (0.00 sec)

mysql> 

gtid_executedをgtid_purgedがセットされました。ダンプファイルに書いてあったのはgtid_purgedだけだったのに?

では公式ドキュメントのgtid_purgedgtid_executedを見てみましょう。以下ざっくりと。

  • gtid_executed
    • 適当にまとめると、globalスコープの場合はgtid_executedに書かれてるgtidまでバイナリログに書いてあるよ、って事だと思います。read_onlyなパラメータで空にしたいならreset master;実行してね。5.6.9より前はgtid_doneってパラメータ名だったよ。
  • gtid_purged
    • こちらも適当にまとめると、gtid_purgedに書かれているgtidまではバイナリログから削除されてるよ。5.6.9より前はgtid_lostってパラメータ名。gtid_executedが空の場合だけset可能だよ。

なんか訳すと矛盾してるような気分になりますが、gtid_executedは設定されているgtidまでは実行済みだよってことだし、新規にダンプファイルから上記の流れでスレーブを作った場合はバイナリログにリストアされた内容は記録されていなかったりします。

# mysqlbinlog ./mysql-bin.000004
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 4
#130309  2:22:35 server id 239  end_log_pos 120 CRC32 0x4fbf920d 	Start: binlog v 4, server v 5.6.10-log created 130309  2:22:35
# Warning: this binlog is either in use or was not closed properly.
BINLOG '
Wx46UQ/vAAAAdAAAAHgAAAABAAQANS42LjEwLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAXAAEGggAAAAICAgCAAAACgoKGRkAAQ2S
v08=
'/*!*/;
# at 120
#130309  2:22:35 server id 239  end_log_pos 191 CRC32 0x28155e76 	Previous-GTIDs
# 71803be2-7816-11e2-8c99-5254003e1cec:1-665
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
#

なんで?と思われた方、上の方で書いたダンプファイルのhead -30の結果を再度見てみましょう。必要なポイントだけ抜粋したのが以下になります。

SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
SET @@SESSION.SQL_LOG_BIN= 0;

SQL_LOG_BIN=0が発行されています。よってリストアで発行された更新処理はバイナリログに記録されません。gtid_purgedがgtid_executedが同じなのは間違いではないことが分かります。

ということでスレーブではgtid_executedに設定されているgtidまで更新内容が反映済みという状態となります。この状態でchange master toでmaster_auto_position=1でレプリケーションを開始します。

mysql> change master to master_host='192.168.1.237', master_user='repl', master_password='repl-pw', master_auto_position=1;                                       
Query OK, 0 rows affected, 2 warnings (0.03 sec)

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: 
                  Master_Host: 192.168.1.237
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: 
          Read_Master_Log_Pos: 4
               Relay_Log_File: mysql-relay-bin.000001
                Relay_Log_Pos: 4
        Relay_Master_Log_File: 
             Slave_IO_Running: No
            Slave_SQL_Running: No
              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: 0
              Relay_Log_Space: 151
              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: NULL
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 0
                  Master_UUID: 
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: 
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 71803be2-7816-11e2-8c99-5254003e1cec:1-665
                Auto_Position: 1
1 row in set (0.00 sec)

mysql> 

正しくExecuted_Gtid_Setにgtidが設定されている事が分かります。

Executed_Gtid_Set: 71803be2-7816-11e2-8c99-5254003e1cec:1-665

ではstart slave;を行いましょう。

mysql> start slave;
Query OK, 0 rows affected (0.02 sec)

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.1.237
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000006
          Read_Master_Log_Pos: 200338
               Relay_Log_File: mysql-relay-bin.000007
                Relay_Log_Pos: 2816
        Relay_Master_Log_File: mysql-bin.000006
             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: 200338
              Relay_Log_Space: 3230
              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: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 237
                  Master_UUID: 71803be2-7816-11e2-8c99-5254003e1cec
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 71803be2-7816-11e2-8c99-5254003e1cec:666-673
            Executed_Gtid_Set: 71803be2-7816-11e2-8c99-5254003e1cec:1-673
                Auto_Position: 1
1 row in set (0.00 sec)

mysql> 

問題無くレプリケーションが開始されました。良いですね。gtid環境でもmysqldumpが普通に使えます。

その他

今回は書きませんが、gtidを使ったSlaveサーバから--dump-slave=2とかでダンプを取った場合も同様に新しいSlaveを作成可能です。但し、その場合はリストアするMySQLは極力真っ新な状態の物を使う事を推奨します。クラッシュセーフなSlaveにするために以下パラメータをTABLEにしてる場合、バイナリログ、リレーログファイルの情報がダンプ元のサーバの情報になってしまうためです。

master_info_repository=TABLE
relay_log_info_repository=TABLE

--dump-slaveを使う事が最初から予定されている場合は全てのMaster, Slaveで以下のようにバイナリログ、リレーログファイル名を統一しておくと良いでしょう。手間が掛からなくなります。

log-bin=mysql-bin
relay_log=mysql-relay-bin

まぁ"ホスト名"-binとかの形には本来はしない方が良い(個人的にホスト名が付くのは非推奨)ので、既にちゃんと指定してるよ、という方も多いかと思います。

また、gtidを利用してない環境にリストアするためにダンプを取得する場合はmysqldumpに--set-gtid-purged=OFFを付けます。あまり無いだろうけど、あえて5.5系とか。このオプションを付ける事でgtidを使用した環境からのダンプファイルに以下内容が出力されなくなります。

SET @@GLOBAL.GTID_PURGED='71803be2-7816-11e2-8c99-5254003e1cec:1-665';

終わりに

MySQL 5.6.10ではgtidを利用した環境でもgtidを特に意識せず、mysqldumpによるダンプファイルから新規にSlaveが作成出来ます。gtid環境でネックだなぁ、と思ってた内容が一つ解消したのは個人的には嬉しい所です。