こんにちは。やまゆです。
今までは mysqldump
コマンドを実行してデータベーススキーマとデータをファイル化したり、パイプして別のインスタンスにリストアしていたと思います(以下は laravel
という DB スキーマの dump/restore を例にします)。
$ mysqldump -uroot -p -h127.0.0.1 -P 3306 --quick --single-transaction laravel | mysql -uroot -p -h127.0.0.1 -P 3306 laravel2
# gzip で圧縮する(ファイルにするので別のインスタンスにファイルで簡単に転送出来る)
$ mysqldump -uroot -p -h127.0.0.1 -P 3306 laravel | gzip > laravel.sql.gz
# 例えば別のインスタンスにファイルを持ってきて
$ zcat laravel.sql.gz | mysql -uroot -p -h127.0.0.1 -P 3306 laravel
しかし、 mysqldump
は非常に古いコマンドで、下記のような課題があります。
- シングルスレッド: 大規模データで時間がかかる
- 圧縮はパイプ任せ (本体に圧縮制御なし)
- 進捗がわかりにくい
- 大量テーブルで巨大な 1 ファイル (部分的リトライがしづらい)
- バージョン間互換性の自動調整が弱い
- 権限/イベント/ルーチン等の制御が煩雑
それらを解決するため MySQL 5.7.8 から mysqlpump
というコマンドが提供されるようになりましたが、これは MySQL 8.4.0 で削除されました。
mysqlsh
という汎用コマンドに吸収されたためです。また、バージョンによって使える挙動が違うので注意してください。さらに、このコマンドは Oracle MySQL にしか搭載されておらず、 MariaDB では利用出来ないので注意が必要です。
ダンプ・リストア先は MySQL 5.7~9.x までと幅広く対応しています(リストアする時にメジャーバージョンが異なる場合はオプション設定しないとエラーになります)。
インストール
デフォルトの圧縮方式に zstd を利用するので、ダンプ・リストアするインスタンスで、事前に各リポジトリ等から apt install zstd
などでインストールをしておいてください。
MySQL :: MySQL Shell 8.0 :: 2.2 Linux への MySQL Shell のインストール
最近だと 8.4 LTS や 9.x Innovation が選択される場合もありますので、バージョンには注意してください(今は 8.4 LTS が推奨)。
$ which mysqlsh
/usr/bin/mysqlsh
$ mysqlsh --version
mysqlsh Ver 8.0.36 for Linux on x86_64 - for MySQL 8.0.36 (Source distribution)
Ubuntu 24.04 では sudo apt install -y mysql-shell
で MySQL 8.0 の mysqlsh
がインストール可能です。
Debian(例えば Docker イメージなど) では MariaDB しかなく mysql-shell
が提供されていないので、代わりに下記に記してある mysql:8.4-oracle(Docker image) を使うのが良いでしょう。
mysqlsh
8.0.x で実行
MySQL :: MySQL Shell 8.0 :: 1 MySQL Shell の機能
スキーマとデータを dump(ファイル化)
$ DB_NAME=laravel
$ TMP_DIR_PATH=$(mktemp -d)
$ DB_DSN=mysql://root:root@127.0.0.1:3306
$ mysqlsh ${DB_DSN} -- util dump-schemas ${DB_NAME} --output-url=${TMP_DIR_PATH} --threads=$(nproc) --defaultCharacterSet="utf8mb4" --tzUtc=false
Acquiring global read lock
Global read lock acquired
Initializing - done
1 schemas will be dumped and within them 950 tables, 30 views.
Gathering information - done
All transactions have been started
Locking instance for backup
Global read lock has been released
Writing global DDL files
Running data dump using 8 threads.
NOTE: Progress information uses estimated values and may not be accurate.
Writing schema metadata - done
Writing DDL - done
Writing table metadata - done
Starting data dump
101% (2.99M rows / ~2.95M rows), 346.95K rows/s, 22.83 MB/s uncompressed, 1.16 MB/s compressed
Dump duration: 00:00:13s
Total duration: 00:00:14s
Schemas dumped: 1
Tables dumped: 950
Uncompressed data size: 198.66 MB
Compressed data size: 10.05 MB
Compression ratio: 19.8
Rows written: 2987248
Bytes written: 10.05 MB
Average uncompressed throughput: 14.66 MB/s
Average compressed throughput: 741.84 KB/s
$ cd ${TMP_DIR_PATH}
$ tar -Izstd -cf laravel.tar.zst .
これで進捗を表示しながら並列でテーブルごとに dump が取れるので、一つのファイルにして転送出来るようになります。 Shell なので REPL モードももちろんありますが、このようにワンライナーで内蔵されているコマンドを実行することも出来ます。
リストア
リストア先は local_infile システム変数が有効になっている必要があります。
SET GLOBAL local_infile=1;
事前に先ほど dump したファイルを展開しておきます。
$ ZSTD_FILE=/home/myuser/laravel.tar.zst
$ TMP_DIR_PATH=$(mktemp -d)
$ cd ${TMP_DIR_PATH}
$ tar -Izstd -zf ${ZSTD_FILE}
$ DB_NAME=laravel
$ DB_DSN=mysql://root:root@127.0.0.1:3306
$ mysqlsh ${DB_DSN} -- util load-dump ${TMP_DIR_PATH} --threads=$(nproc) --analyzeTables="on" --characterSet="utf8mb4" --schema=${DB_NAME} --ignoreVersion=true
Loading DDL and Data from '/tmp/xxxxxx' using 8 threads.
Opening dump...
Target is MySQL 5.7.42-log. Dump was produced from MySQL 8.0.43
WARNING: Destination MySQL version is older than the one where the dump was created. Loading dumps from different major MySQL versions is not fully supported and may not work. The 'ignoreVersion' option is enabled, so loading anyway.
Scanning metadata - done
Checking for pre-existing objects...
Executing common preamble SQL
Executing DDL - done
Executing view DDL - done
Starting data load
1 thds loading \ 100% (198.65 MB / 198.65 MB), 1.49 MB/s, 961 / 961 tables and partitions done
Recreating indexes - done
Executing common postamble SQL
Analyzing tables - done
No data loaded.
0 warnings were reported during the load.
analyzeTables
を有効にすると、リストア後に自動的に ANALYZE TABLE foo;
を実行してくれます。クエリの実行計画が最適化されるのでオススメです。
また、 ignoreVersion
を有効にすると、例えば 5.7 から 8.0、 8.0 から 8.4 など dump/restore 間で大きなバージョンの違いがあっても無視してリストアするようになります。上記の出力例では Target is MySQL 5.7.42-log. Dump was produced from MySQL 8.0.43
と出ていますね(8.0.43 の dump を 5.7.42 に展開しようとした)。
mysqlsh
8.4.x(Docker) で実行
MySQL :: MySQL Shell 8.4 :: 1 MySQL Shell Features
MySQL 8.4 バージョンはさらに機能が追加されて、同じ場所で双方の MySQL インスタンスに接続出来る場合は、一発で SRC から DEST に転送することが可能になり、一時ファイルを置くスペースが不要になります。また、 checksum が追加されたので適切にデータを転送出来たかもわかります。
$ docker run --rm -it --net="host" mirror.gcr.io/mysql:8.4 sh
# DB_NAME=laravel
# DB_DSN_FROM=mysql://root:root@127.0.0.1:3308
# DB_DSN_TO=mysql://root:root@127.0.0.1:3306
# THREADS=$(nproc)
# cat <<EOL | mysqlsh ${DB_DSN_FROM} --js
util.copySchemas(["${DB_NAME}"], "${DB_DSN_TO}", {
threads: ${THREADS},
defaultCharacterSet: "utf8mb4",
checksum: true,
ignoreVersion: true,
handleGrantErrors: "ignore",
tzUtc: false,
analyzeTables: "on",
ignoreExistingObjects: true,
});
EOL
ホスト側で見える MySQL インスタンスと同じにするため、今回は docker のオプションに --net="host"
を追加しています。
本当は前の例と同様ワンライナーがいけるはずなんですが、バグなのか私の環境では入力フォーマットエラーになってしまったので、 JavaScript での命令をパイプで入力しています(実は MySQL Shell では SQL/JavaScript/python が利用可能です)。
各オプションの詳細については公式ページをご覧ください。
mysqldump
からmysqlsh
への移行で並列化・圧縮・進捗表示・整合性確認が一度に解決- 8.4 以降はストリーミングコピー + チェックサムで中間ファイル不要
- バージョン差異がある復元では
--ignoreVersion
とログ確認が必須 - 運用面では権限・パフォーマンス調整・検証手順をテンプレ化しておくと再現性が向上
この方式でも、転送には zstd 圧縮が用いられるので、より高速かつ転送量を抑えて dump/restore が可能になります。
また、 dump はオプション指定で宛先を Oracle Cloud Infrastructure のオブジェクト・ストレージか、 S3 API に互換性のある任意のサービスに出力することも出来ます。クラウドストレージへのバックアップが簡単になりますね。
他の選択肢
mysql-shell を使えない場合の選択肢としては、 Percona XtraBackup を用いるという手段もあります。ただ、こちらは dump/restore する MySQL が動作しているそれぞれのインスタンスにインストールする必要があるため、少し条件が厳しくなるかなと思います。
(知ってますか? MySQL 8.0 はもうすぐメンテナンスアップデートも終わって、 8.4 LTS にアップグレードする必要が出てきますよ…?)
運用・保守案件ではまだ全然 dump/restore が現役なインフィニットループではいつでも技術者を募集しています!興味がある方は採用情報ページをご覧ください!