MySQL / MariaDB レプリケーション障害|エラー1236でSlaveが停止した場合の復旧手順

MySQL / MariaDB レプリケーション障害(エラー1236)復旧手順(Master→Slave)

 こんにちは、レムシステム株式会社のエンジニア、小村(@system_kom)です。MySQL / MariaDBのレプリケーション(Master→Slave)は、参照系の負荷分散やDR用途でよく使われますが、障害発生時にbinlogの不整合が起きると、Slaveが再開できず停止してしまうことがあります。

 本記事では、当社で対応したMariaDBレプリケーション環境でSlave側が停止(エラー1236)した事例をもとに、調査の観点と、データ整合性を担保した復旧手順(再同期)を、コピーして使えるコマンド付きでまとめます。

 なお本記事の例は、サーバOSにCentOS 7系、DBはMySQL 5系(互換系を含む)を前提としています。CentOS 7はサポート終了(EOL)していますが、ここで紹介する復旧手順(SHOW SLAVE STATUSでの状況確認、mysqldumpによる再同期、CHANGE MASTER TOでの再開)は、RHEL系/Ubuntu/Debianなど他ディストリビューションでも同様に適用できます(コマンドや設定ファイルのパスが一部異なる程度です)。

構成(例)
・Master:192.0.2.10(MySQL 5系 / CentOS 7系)
・Slave:192.0.2.20(MySQL 5系 / CentOS 7系)
※ 記載のIPは説明用のダミー値です

SlaveのIOスレッドが停止しレプリケーション不能

 障害後、Slave側で状態を確認したところ、Slave_IO_Running が No となり、Seconds_Behind_Master が NULL の状態でした。まずは SHOW SLAVE STATUS で、どのスレッドが停止しているか、どのエラーが出ているかを把握します。MySQL / MariaDB のレプリケーション障害では、ここで表示される Last_IO_Error / Last_SQL_Error が初動判断の材料になります。

主な兆候(例)
・Slave_IO_Running: No
・Seconds_Behind_Master: NULL
・Last_IO_Error に 1236 が出る

 今回は、Slave側がMasterのbinlogを読み込もうとした際に、要求した位置がMaster側のファイルサイズを超えており、エラー1236でIOスレッドが停止していました。コマンドと結果は、次のような形で確認できます。

-- Slave側
$ mysql -u admin_user -p ****
mysql > SHOW SLAVE STATUS\G;
*************************** 1. row ***************************
               Slave_IO_State: 
                  Master_Host: 192.0.2.10
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: smtp-bin.000048
          Read_Master_Log_Pos: 868470877
               Relay_Log_File: y02-relay-bin.001757
                Relay_Log_Pos: 4
        Relay_Master_Log_File: smtp-bin.000048
             Slave_IO_Running: No <= Noになっている
            Slave_SQL_Running: Yes
              Replicate_Do_DB: sample_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: 868470877
              Relay_Log_Space: 154
              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: 1236
                Last_IO_Error: Got fatal error 1236 from master when reading data from binary 
                log: 'Client requested master to start replication from position > file size'
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 1001
                  Master_UUID: 408a1508-dc51-11ea-9f0b-00135d111783
             Master_Info_File: /var/lib/mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 250116 10:28:34
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)
Last_IO_Errno: 1236
Last_IO_Error: Got fatal error 1236 from master when reading data from binary log:
'Client requested master to start replication from position > file size'

binlog位置不整合と単純な位置合わせの限界

 今回の事象では、Master側で長時間の停止が発生した後にサービスを再開したことで、binlogファイルの世代が進み、Slave側が保持しているMASTER_LOG_FILE / MASTER_LOG_POS と整合が取れなくなっていました。

 この状態でSlaveは、既に存在しない、またはサイズを超えた位置からbinlogを読み込もうとするため、エラー1236が発生し、IOスレッドが停止します。実運用では、Master停止期間中のbinlogローテーション設定や保持期間、ディスク逼迫なども絡むため、「なぜその位置が無いのか」まで一度は確認しておくのが安全です。

 初動対応としてよく行われるのが、Master側の現在のbinlog位置を取得し、Slave側で CHANGE MASTER TO を用いて位置を合わせる方法です。一見すると正常復旧したように見えるケースもありますが、データ差分が存在する場合は、後続のSQL適用フェーズで再度停止する可能性があります。

-- Master側
$ mysql -u admin_user -p ****
mysql > SHOW MASTER STATUS;
+-----------------+----------+--------------+------------------+-------------------+
| File            | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------------+----------+--------------+------------------+-------------------+
| smtp-bin.000051 | 14807416 |              |                  |                   |
+-----------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

-- Slave側
$ mysql -u admin_user -p ****
mysql > STOP SLAVE;
Query OK, 0 rows affected (0.04 sec)
mysql> exit

mysql > CHANGE MASTER TO
    MASTER_HOST='192.0.2.10',
    MASTER_LOG_FILE='smtp-bin.000051',
    MASTER_LOG_POS=14807416;
Query OK, 0 rows affected (0.13 sec)

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.0.2.10
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: smtp-bin.000051
          Read_Master_Log_Pos: 14817359
               Relay_Log_File: y02-relay-bin.000002
                Relay_Log_Pos: 10262
        Relay_Master_Log_File: smtp-bin.000051
             Slave_IO_Running: Yes <=Yes
            Slave_SQL_Running: Yes <=Yes
              Replicate_Do_DB: sample_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: 14817359 <=Masterと同じことを確認
              Relay_Log_Space: 10467
              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: 1001
                  Master_UUID: 408a1508-dc51-11ea-9f0b-00135d111783
             Master_Info_File: /var/lib/mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           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: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)

 位置合わせだけではMaster/Slave間にデータ差異が残っている場合に、1032エラー等で停止してしまうことがあります。特に業務DBでは、差分が数件でも「帳票が合わない」「集計がずれる」といった形で後から発覚し、障害対応が長期化することがあります。そのため、当社では「再開できた」ことよりも、「整合性が取れている」ことを重視します。

MasterのデータをダンプしてSlaveを再同期する(確実な復旧)

 レムシステムでは、レプリケーション障害が発生した場合、「とりあえず動かす」よりも「データ整合性を完全に担保する」ことを優先します。

 特に業務DBでは、数件の差分や論理的不整合が後工程で大きな問題になることがあります。そのため今回の事例では、Slaveを部分的に復旧させるのではなく、Masterのデータを正として再同期する方針を採用しました。

 この方法は一時的に作業量が増えますが、再停止や不整合の再発を防げるため、結果として復旧時間と運用リスクを最小化できます。加えて、再同期を前提にすることで、復旧後の監査・報告(いつ、どの時点のデータを正としたか)も明確になります。

 ここからは、実際に採用した整合性重視の復旧手順を、作業の流れに沿ってまとめます。ポイントは、ダンプ取得時点のbinlog位置を揃え、Slaveへインポートしてからレプリケーションを再開することです。

Master側でロック→ステータス取得→ダンプ

 更新が走る業務DBの場合、ダンプ取得中に更新が進むと整合性が崩れます。そこで、短時間でREAD LOCKを取り、SHOW MASTER STATUSでbinlog位置を記録した上でダンプを取得します(運用要件により --single-transaction の利用可否は判断します)。

 READ LOCKは書き込みを止めるため、実施する時間帯(深夜・メンテナンス時間など)や、業務影響の有無を事前に確認しておくことが重要です。停止時間を最小化するためにも、ダンプ先の空き容量や転送手順は事前に準備しておくとスムーズです。

-- Master側

$ mysql -u admin_user -p ****

mysql > FLUSH TABLES WITH READ LOCK;
mysql > SHOW MASTER STATUS;
+-----------------+----------+--------------+------------------+-------------------+
| File            | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------------+----------+--------------+------------------+-------------------+
| smtp-bin.000051 | 15187012 |              |                  |                   |
+-----------------+----------+--------------+------------------+-------------------+
1 row in set (0.02 sec)

 続けて、対象DBをダンプします。ここでは例として、対象DBを sample_db としています。

-- Master側
$ mysqldump sample_db --add-drop-table --master-data=2 -u admin_user -p > /tmp/sample_db.sql

 ダンプ取得後はロックを解除します。ロック解除を忘れると、Master側の書き込みが止まったままになり業務影響が発生するため、作業手順書には「解除確認」まで含めておくのが安全です。

mysql > UNLOCK TABLES;
mysql > exit

Slave側で停止→インポート→CHANGE MASTER→再開

 次にSlave側でレプリケーションを停止し、Masterから取得したダンプをインポートします。ここで重要なのは、「一度STOP SLAVEしてから」インポートすることです。動作中のまま投入すると、relay log適用とデータ投入が競合し、状態がさらに複雑化することがあります。

 また、インポート対象のDBが既に存在する場合は、ダンプオプションや運用ルールに応じて、事前にバックアップを取った上でDROP/CREATEの判断を行います。今回は --add-drop-table を含むダンプを前提とした手順です。

-- Slave側

$ mysql -u admin_user -p ****

mysql > STOP SLAVE;
mysql > exit

-- ダンプ投入
$ mysql -u admin_user -p sample_db < /tmp/sample_db.sql

 最後に、Master接続情報と、ダンプ取得時点のbinlog位置を指定してレプリケーションを再開します。binlog位置は、ダンプファイル内(--master-data により埋め込まれるCHANGE MASTER相当の記述)を参照して設定する運用にすると、作業ミスを減らせます。

-- Slave側

$ mysql -u admin_user -p ****

mysql > CHANGE MASTER TO
    MASTER_HOST='192.0.2.10',
    MASTER_USER='repl_user',
    MASTER_PASSWORD='********',
    MASTER_LOG_FILE='smtp-bin.000051',
    MASTER_LOG_POS=15187012;
Query OK, 0 rows affected, 2 warnings (0.11 sec)

mysql > START SLAVE;
Query OK, 0 rows affected (0.03 sec)

mysql > SHOW SLAVE STATUS\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.0.2.10
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: smtp-bin.000051
          Read_Master_Log_Pos: 15194048
               Relay_Log_File: y02-relay-bin.000002
                Relay_Log_Pos: 7355
        Relay_Master_Log_File: smtp-bin.000051
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: sample_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: 15194048
              Relay_Log_Space: 7560
              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: 1001
                  Master_UUID: 408a1508-dc51-11ea-9f0b-00135d111783
             Master_Info_File: /var/lib/mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           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: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
1 row in set (0.00 sec)

確認ポイント(復旧判定)

 ダンプのインポートと CHANGE MASTER TO による再設定が完了したら、必ずレプリケーションの状態を確認します。

 単に START SLAVE がエラーなく実行できただけでは、復旧が完了したとは言えません。IOスレッド・SQLスレッドの両方が正常に動作し、Masterとの差分が解消されていることを確認する必要があります。

 特に、障害直後は「IOだけ復旧してSQLが止まっている」「遅延が増え続けている」「エラーが出たり消えたりする」など、微妙な状態になることがあります。表面的に動いていても、業務ピークで再停止するケースもあるため、最低限の指標を押さえて判定するのがおすすめです。

 以下の表は、最低限チェックすべきレプリケーションの状態項目です。運用監視のチェック項目としても、そのまま流用できます。

確認項目期待値意味
Slave_IO_RunningYesMasterからbinlogを取得できている
Slave_SQL_RunningYesrelay logを適用できている
Seconds_Behind_Master0(または許容範囲)遅延が解消している
表:レプリケーション復旧の確認ポイント

まとめ:レプリケーション復旧は「整合性重視」で

 MariaDB / MySQL レプリケーション障害では、表面的に動かすだけなら position 再指定で復旧する場合もありますが、実運用ではデータ差異が残ると再停止しがちです。今回の事例では、Masterのデータを正としてダンプ→再同期→再開することで、安定稼働に戻せました。

 レムシステムでは、MySQL / MariaDB のバックアップ設計・レプリケーション設計・障害対応まで含めて支援しています。障害時の初動整理、復旧手順の作成、再発防止(監視や保守設計)まで対応可能です。状況整理だけでも構いませんので、お困りの際はお気軽にご相談ください。

\ MySQL・MariaDBの障害対応/運用改善のご相談はこちら /

無料で相談してみる 見積もりを依頼する

-Linux, オープンソース