🚀

MySQL 銀の弾丸 clone プラグイン

に公開

MySQL 運用の銀の弾丸 clone プラグイン

MySQL の clone プラグインは「エラーで原因がわからないけど全部消してやり直したい」を可能にするプラグインです。

clone プラグインは概ね以下のように動作します。

処理 内容
削除 コピー先のデータをすべて消去する
コピー コピー先からコピー元へ直接接続してコピー元の全DBデータをコピーする
再起動 コピー先の MySQL を再起動する
その他 レプリケーション再開のための position や GTID を設定する

clone によるトラブルシューティングは、アプリレベルにおける再起動によるエラー解消に近いです。アプリケーションの再起動で多くの問題を解消できる理由は、再起動によってメモリ上のデータ構造の不整合をリセットできるからです。

それと同じように MySQL では clone 操作によってデータ不整合をリセットすることで多くの問題を解消できます。
MySQL も再起動をすればメモリ上の問題を解消できますが、MySQL にはストレージ層もあるので、ストレージ層までリセットするためには clone 操作が必要というわけです。

clone プラグインが解決できる運用上のトラブルはとても幅広いです。
例えば以下のようなケースは clone プラグインで解決するのが最も簡単な場合が多いです。

  1. Replica SQLスレッドがエラーになり、レプリケーションが停止してしまった
  2. レプリケーション遅延がなかなか解消しない
  3. 必要な binlog がソースから purge されていてレプリケーションを再開できない
  4. オペミスによりレプリカのデータを破損してしまった
  5. 新しいレプリカを追加したい
  6. ある MySQL のデータを別の MySQL に移行したい (同一バージョン限定)

うん... もう全部 clone でいいんじゃないかな?

ざっくりくくっているので clone で解決しないケースや、clone より適切な解決方法があるケースもあります。

いくつかのケースについて詳しく見てみましょう。

Replica SQLスレッドのエラー

Replica SQLスレッドのエラーは clone プラグインで解消するのが簡単です。
もし Replica SQLスレッドの エラー原因のトランザクションを無視していいことが自明なのであれば GTID スキップによる復旧が最も速いです。

https://zenn.dev/cotox2/articles/skip-error-gtid

ただ、多くの場合は Replica SQLスレッドのエラー原因となった GTID を特定して、その GTID がスキップして問題ないトランザクションなのかどうかを調べる作業に労力がかかります。さらにその GTID を安全にスキップできたとしても、後続の GTID で次々とSQLスレッドがエラーになるケースもあり、こうなると途方に暮れます。

したがって、clone によるコピー時間MySQL の再起動 を許容できるのであれば clone で解消してしまうのが最も簡単で安全です。

レプリケーション遅延がなかなか解消しない

レプリケーション遅延の原因はさまざまですが、そのうち Replica SQLスレッドにおいてレプリカ側での更新の適用に時間がかかっているケースについては、レプリケーションを止めて clone したほうが速く解消できる場合があります。

これは clone が物理バックアップによるリストアを実行するためです。

一般的に論理バックアップによるリストアよりも物理バックアップによるリストアのほうが高速なことが多いです。これは論理バックアップのリストアには再計算を必要とするのに対して、物理バックアップのリストアは計算結果を直接リストアしており、再計算の必要がないからです。

clone が物理バックアップのリストア処理であるのに対して、MySQLのレプリケーションは差分を論理バックアップによってリストアする処理に相当すると考えることができます。

特にハードウェア障害などによりレプリカが長時間ダウンしてしまった場合、レプリカの復旧後にレプリケーションが追いつくのを待っていると非常に時間が掛かるケースがあります。こういったケースでは clone プラグインによる復旧を試す価値があります。

もちろんネットワーク帯域などによっては clone のほうが遅いこともあります。
clone はDB全体のデータを丸ごとコピーする操作なので、ネットワーク帯域が細い環境では clone のほうが時間がかかるかもしれません。

(全データの転送にかかる時間) \geq (差分データの転送と再計算にかかる時間)

が成り立つ場合には clone のほうが速いことになります。

必要な binlog がソースから purge されていてレプリケーションを再開できない

MySQL のレプリケーションのためには、レプリカ側が反映していない更新の内容をソース側が binlog として保存している必要があります。

しかし、binlog は binlog_expire_logs_seconds 変数に指定された秒数が経過すると自動的に削除されます。(binlog purge) そのため、ハードウェア障害などにより長時間レプリカが停止した場合には復旧した時点では既にソース側が必要な binlog を purge してしまっている場合があります。こうなってしまうとレプリケーションを再開することができません。

このようなケースでも clone プラグインでレプリケーションを復旧できます。clone プラグインを利用すればバックアップデータをどこかに保存して転送したりする必要はありません。さらに clone プラグインはレプリケーションを再開するためのセットアップまでしてくれます。

clone プラグインには明らかに MySQL の運用を楽にしようという設計思想が見えます。

clone プラグインの利用方法

clone プラグインのセットアップと利用方法を見てみましょう。

まず、コピー元とコピー先の両方に clone プラグインをインストールします。
SONAME には clone プラグインのファイル名を指定します。MySQL 8.0.17 以降のMySQLの公式パッケージを利用しているのであれば mysql_clone.so が同梱されているはずなので、そのまま下記のクエリを実行するだけでインストールできます。

INSTALL PLUGIN clone SONAME 'mysql_clone.so';

次に、コピー元で clone 用ユーザを作成し、BACKUP_ADMIN 権限を付与します。

CREATE USER clone_user@'%' IDENTIFIED BY 'clonepass';
GRANT BACKUP_ADMIN  ON *.* TO clone_user@'%';

コピー先で clone_valid_donor_list 変数にコピー元ホストを指定します。
clone_valid_donor_list に指定されていないホストからはデータを clone することができません。

SET GLOBAL clone_valid_donor_list = 'mysql-source:3306';

ここまでで clone を実行する準備が完了です。
あとはコピー先で以下のように実行することで clone を実行できます。

CLONE INSTANCE FROM clone_user@'mysql-source':3306 IDENTIFIED BY 'clonepass';

clone の実行が完了すると clone plugin は自動的にMySQLサーバの再起動を試みます。
MySQL再起動後の RECOVERY 処理まで完了すれば clone 成功です。

clone の進捗は performance_schema.clone_progress テーブルで確認できます。

select * from performance_schema.clone_progress;

巨大なデータを clone する場合には以下のように進捗率やデータサイズの整形を表示するようにしておくと進捗が把握できるので安心できます。

SELECT
    ID,
    STAGE,
    STATE,
    BEGIN_TIME,
    END_TIME,
    THREADS,
    -- 進捗率(%)
    CASE
        WHEN ESTIMATE = 0 THEN '100.00%'
        ELSE CONCAT(FORMAT((DATA / ESTIMATE) * 100, 2), '%')
    END AS PROGRESS_RATE,
    -- GB単位でカンマ区切り
    CONCAT(FORMAT(ESTIMATE / 1024 / 1024 / 1024, 2), ' GB') AS ESTIMATE_GB,
    CONCAT(FORMAT(DATA / 1024 / 1024 / 1024, 2), ' GB') AS DATA_GB,
    CONCAT(FORMAT(NETWORK / 1024 / 1024 / 1024, 2), ' GB') AS NETWORK_GB,
    DATA_SPEED,
    NETWORK_SPEED
FROM
    performance_schema.clone_progress;

実行してみると以下のように表示されます。
出力を見ると DROP DATA のステップが完了しており、既存データの削除は完了していることがわかります。
現在は FILE COPY のステップを実行中で、進捗率は22%程度、約12GBのデータのうち約2.8GBのコピーが完了したということがわかります。

clone の進捗確認
+------+-----------+-------------+----------------------------+----------------------------+---------+---------------+-------------+---------+------------+------------+---------------+
| ID   | STAGE     | STATE       | BEGIN_TIME                 | END_TIME                   | THREADS | PROGRESS_RATE | ESTIMATE_GB | DATA_GB | NETWORK_GB | DATA_SPEED | NETWORK_SPEED |
+------+-----------+-------------+----------------------------+----------------------------+---------+---------------+-------------+---------+------------+------------+---------------+
|    1 | DROP DATA | Completed   | 2026-01-12 12:56:26.737993 | 2026-01-12 12:56:26.969514 |       1 | 100.00%       | 0.00 GB     | 0.00 GB | 0.00 GB    |          0 |             0 |
|    1 | FILE COPY | In Progress | 2026-01-12 12:56:26.969567 | NULL                       |       2 | 22.37%        | 12.67 GB    | 2.83 GB | 2.83 GB    |   96266108 |      96271249 |
|    1 | PAGE COPY | Not Started | NULL                       | NULL                       |       0 | 100.00%       | 0.00 GB     | 0.00 GB | 0.00 GB    |          0 |             0 |
|    1 | REDO COPY | Not Started | NULL                       | NULL                       |       0 | 100.00%       | 0.00 GB     | 0.00 GB | 0.00 GB    |          0 |             0 |
|    1 | FILE SYNC | Not Started | NULL                       | NULL                       |       0 | 100.00%       | 0.00 GB     | 0.00 GB | 0.00 GB    |          0 |             0 |
|    1 | RESTART   | Not Started | NULL                       | NULL                       |       0 | 100.00%       | 0.00 GB     | 0.00 GB | 0.00 GB    |          0 |             0 |
|    1 | RECOVERY  | Not Started | NULL                       | NULL                       |       0 | 100.00%       | 0.00 GB     | 0.00 GB | 0.00 GB    |          0 |             0 |
+------+-----------+-------------+----------------------------+----------------------------+---------+---------------+-------------+---------+------------+------------+---------------+
7 rows in set (0.00 sec)

clone の成功後、コピー先のMySQLは SOURCE_AUTO_POSITION=1 の設定でそのままレプリケーションを開始できる状態になっています。(GTID ベースのレプリケーションの場合)

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='mysql-source',
  SOURCE_USER='repl',
  SOURCE_PASSWORD='replpass',
  SOURCE_AUTO_POSITION=1,
  GET_SOURCE_PUBLIC_KEY=1;

START REPLICA;

つまり clone の完了後にはGTIDベースのレプリケーションを組んで起動するだけでサービスを再開できるということです。

検証環境

Docker Hub の公式 MySQL8.0 イメージを利用しました。

項目
MySQL version 8.0.44
Docker base image mysql:8.0 (Docker Hub 公式)

GTIDをONにしたレプリケーション構成を my.cnf に設定しました。

my.cnf
[mysqld]
log_bin=mysql-bin
binlog_format=ROW
gtid_mode=ON
enforce_gtid_consistency=ON

Discussion