今日はなにの日。

気になったこと勉強になったことのメモ。

今日は、話題になってたMySQLのバックアップ方法について比較してみたの日。

目次

とある日

Twitterで「mysqlpump」が話題になってました。

「mysqlpump」の名前は聞いたことありますが使ったことはありませんでした。

またその中で、MySQL Shellのダンプロードユーティリティを使うというリプがありました。

初めて聞いたので、「mysqlpump」と一緒に使ってみようということで使ってみた記録です。

MySQL のバックアップ方法

リプにありますが、MySQLでバックアップする方法は以下のパターンがあります。

  1. mysqldump
  2. mysqlpump
  3. MySQL Shellのダンプロードユーティリティ
  4. XtraBackup
  5. Clone Plugin

今回は1,2,3の3つを使って比べてみます。

各バックアップ方法のおさらい

軽く各方法について触れておきます。

リプにも各方法のメリデメとかはあります、とても参考になるのでぜひご覧ください。

mysqldump — データベースバックアッププログラム

「mysqldumpコマンド」を使った方法です。

MySQL :: MySQL 8.0 リファレンスマニュアル :: 4.5.4 mysqldump — データベースバックアッププログラム

一般的によく使われるバックアップかなと思います。

個人的に使ってみたメリット

  • 手軽に利用できる
  • ドキュメントが豊富
  • 他の方法に比べてオプションが豊富
  • 一つのファイルに纏められる

mysqlpump — データベースバックアッププログラム

よくTypoと間違われるやつです。

自分もはじめは「え?Typoかな?」と思いました。

MySQL :: MySQL 8.0 リファレンスマニュアル :: 4.5.6 mysqlpump — データベースバックアッププログラム

個人的に使ってみたメリット

  • mysqldumpと同じオプションで使える
  • ダンプ速度はmysqldumpより早い
  • ダンプの進捗状況が見れる

「mysqlpump」については以下記事がとても参考になります。

第153回 mysqlpumpを使ってバックアップを取ってみる | gihyo.jp

mysqldumpより、はやいらしいですが、今回の検証ではリストアにかなり時間がかかりました。

あと、ダンプファイルにデータベース名が明示的に書かれているので別のデータベース先にリストアしたい場合は使えないかもです。

(オプションで変更できそうには見えなかったです、何かご存じの方がいれば教えてください)

以下は、mysqlpumpでダンプしたファイルの一部です。

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `world_x` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
CREATE TABLE `world_x`.`city` (
`ID` int NOT NULL AUTO_INCREMENT,
`Name` char(35) NOT NULL DEFAULT '',
`CountryCode` char(3) NOT NULL DEFAULT '',
`District` char(20) NOT NULL DEFAULT '',
`Info` json DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=16776941 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
;

MySQL Shellのダンプロードユーティリティ

MySQL Shell を使った方法です。

裏ではLOAD DATA LOCAL INFILE ステートメントが利用されるようです。

MySQL :: MySQL Shell 8.0 :: 8.6 ダンプロードユーティリティ

MySQL :: MySQL Shell 8.0 :: 8.5 インスタンスダンプユーティリティ、スキーマダンプユーティリティおよびテーブルダンプユーティリティ

個人的に使ってみたメリット

  • ダンプ・リストアが早い
  • dry run できる
  • ダンプの進捗状況が見れる
  • ファイル容量を圧縮できる

使った感じかなり癖がありますが、速度自体はかなり早かったです。

ダンプするのは、 util.dumpInstance()を使いました、ロードするのはutil.loadDump()を使いました。

それぞれの詳細については以下記事がとても参考になりました。

MySQL Shell で バックアップとリストアをパラレルで実行する – スマートスタイル技術ブログ

MySQL Shell Dump UtilityとDump Loading Utilityの使い方 - Qiita

バックアップやってみた

前提条件

MySQLのサンプルデータベースを使ってます。

MySQL :: Other MySQL Documentation

データ量はある程度見栄えがあるように2GBぐらいに増やしてます。

(root@localhost) [world_x] 8.0.32 > SELECT       table_name, engine, table_rows AS tbl_rows,     avg_row_length AS rlen,       floor((data_length+index_length)/1024/1024) AS allmb,       floor((data_length)/1024/1024) AS dmb,       floor((index_length)/1024/1024) AS imb    FROM      information_schema.tables   WHERE     table_schema=database()   ORDER BY     (data_length+index_length) DESC;
+-----------------+--------+----------+------+-------+------+------+
| TABLE_NAME      | ENGINE | tbl_rows | rlen | allmb | dmb  | imb  |
+-----------------+--------+----------+------+-------+------+------+
| city            | InnoDB | 16587999 |  120 |  1909 | 1909 |    0 |
| countrylanguage | InnoDB |      984 |   99 |     0 |    0 |    0 |
| countryinfo     | InnoDB |      239 |  616 |     0 |    0 |    0 |
| country         | InnoDB |      239 |  205 |     0 |    0 |    0 |
+-----------------+--------+----------+------+-------+------+------+
4 rows in set (0.11 sec)

docker compose で以下環境のもとやってます。

  db7:
    image: mysql:8.0.32
    container_name: mysql_ver_8.0.32
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: test
      MYSQL_USER: docker
      MYSQL_PASSWORD: docker
      TZ: "Asia/Tokyo"
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - ../../MySQL/settings/my.cnf:/etc/mysql/conf.d/my.cnf
      - ../../MySQL/settings/mylogin.sh:/root/mylogin.sh

my.cnf

# MySQLサーバーへの設定
[mysqld]
# 文字コード/照合順序の設定
character-set-server = utf8mb4
collation-server = utf8mb4_bin

# タイムゾーンの設定
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# デフォルト認証プラグインの設定
default-authentication-plugin = mysql_native_password
# エラーログの設定

# スロークエリログの設定
slow_query_log = 1
# long_query_time = 5.0
log_queries_not_using_indexes = 0

# 実行ログの設定
general_log = 1
local_infile = 1

# mysqlオプションの設定
[mysql]
# 文字コードの設定
default-character-set = utf8mb4
prompt="(\\U) [\\d] \\v \\T> "
#prompt="(\\u@\\h) [\\d]> "
#prompt="\\r:\\m:\\s> "

# mysqlクライアントツールの設定
[client]
# 文字コードの設定

mysqldump

ダンプ

bash-4.4# time mysqldump -u root world_x > world_x_mysqldump.sql

real    0m23.256s
user    0m11.475s
sys     0m2.065s
bash-4.4# ll
bash: ll: command not found
bash-4.4# ls -l
total 1093816
-rw-r--r-- 1 root root 1120062119 Mar 26 17:25 world_x_mysqldump.sql
bash-4.4#

ロード

bash-4.4# time mysql -u root world_x_dump < world_x_mysqldump.sql

real    4m27.495s
user    0m6.608s
sys     0m0.579s

mysqlpump

ダンプ

bash-4.4# time mysqlpump -uroot world_x > world_x_mysqlpump.sql
Dump progress: 0/3 tables, 250/16401493 rows
Dump progress: 3/4 tables, 838734/16402477 rows
Dump progress: 3/4 tables, 1710484/16402477 rows
Dump progress: 3/4 tables, 2517984/16402477 rows
Dump progress: 3/4 tables, 3388734/16402477 rows
Dump progress: 3/4 tables, 4272984/16402477 rows
Dump progress: 3/4 tables, 5238984/16402477 rows
Dump progress: 3/4 tables, 6129234/16402477 rows
Dump progress: 3/4 tables, 6960984/16402477 rows
Dump progress: 3/4 tables, 7815484/16402477 rows
Dump progress: 3/4 tables, 8637984/16402477 rows
Dump progress: 3/4 tables, 9572234/16402477 rows
Dump progress: 3/4 tables, 10502484/16402477 rows
Dump progress: 3/4 tables, 11338484/16402477 rows
Dump progress: 3/4 tables, 12300734/16402477 rows
Dump progress: 3/4 tables, 13147234/16402477 rows
Dump progress: 3/4 tables, 14051484/16402477 rows
Dump progress: 3/4 tables, 14864234/16402477 rows
Dump progress: 3/4 tables, 15814734/16402477 rows
Dump progress: 3/4 tables, 16693734/16402477 rows
Dump completed in 19908

real    0m19.934s
user    0m14.082s
sys     0m1.465s
bash-4.4# ls -l
total 2190020
drwxr-xr-x 2 root root       4096 Mar 26 17:26 mysqlsh
-rw-r--r-- 1 root root 1120062119 Mar 26 17:25 world_x_mysqldump.sql
-rw-r--r-- 1 root root 1122503879 Mar 26 17:29 world_x_mysqlpump.sql
bash-4.4#

ロード

bash-4.4# time mysql -u root < world_x_mysqlpump.sql

real    15m29.967s
user    0m9.765s
sys     0m3.871s

MySQL Shell

ダンプロードユーティリティでは LOAD DATA LOCAL INFILE ステートメントが使用されるため、インポート中は、ターゲット MySQL インスタンスlocal_infile システム変数のグローバル設定を ON にする必要があります。 デフォルトでは、このシステム変数は標準の MySQL DB システム構成で ON に設定されています。

my.cnfで「1」を指定してます。

ダンプ

めちゃくちゃ色々表示されてて便利だなと思いました。

 MySQL  localhost  JS > util.dumpSchemas(["world_x"], "/home")
Acquiring global read lock
Global read lock acquired
Initializing - done
1 schemas will be dumped and within them 4 tables, 0 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 4 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% (5.54K rows / ~5.43K rows), 0.00 rows/s, 0.00 B/s uncompressed, 0.00 B/s compressed
Dump duration: 00:00:00s
Total duration: 00:00:00s
Schemas dumped: 1
Tables dumped: 4
Uncompressed data size: 319.60 KB
Compressed data size: 97.10 KB
Compression ratio: 3.3
Rows written: 5541
Bytes written: 97.10 KB
Average uncompressed throughput: 319.60 KB/s
Average compressed throughput: 97.10 KB/s

ロード

 MySQL  localhost  JS > util.loadDump("/home/mysqlsh", {schema:"world_x_sh"})
Loading DDL and Data from '/home/mysqlsh' using 4 threads.
Opening dump...
Target is MySQL 8.0.32. Dump was produced from MySQL 8.0.32
Scanning metadata - done
Checking for pre-existing objects...
Executing common preamble SQL
Executing DDL - done
Executing view DDL - done
Starting data load
3 thds loading / 100% (919.52 MB / 919.52 MB), 1.70 MB/s, 4 / 4 tables done
Recreating indexes - done
Executing common postamble SQL
35 chunks (16.71M rows, 919.52 MB) for 4 tables in 1 schemas were loaded in 4 min 57 sec (avg throughput 3.12 MB/s)
0 warnings were reported during the load.

dry run できるのいいなと思いました。

 MySQL  localhost  JS > util.dumpSchemas(["world_x"], "/home/test", {dryRun: true})
dryRun enabled, no locks will be acquired and no files will be created.
Acquiring global read lock
Global read lock acquired
Initializing - done
1 schemas will be dumped and within them 4 tables, 0 views.
Gathering information - done
All transactions have been started
Locking instance for backup
Global read lock has been released
Writing global DDL files
Writing DDL - done
Starting data dump
0% (0 rows / ~15.69M rows), 0.00 rows/s, 0.00 B/s uncompressed, 0.00 B/s compressed

テーブル毎にファイルが作られるようです。

bash-4.4# ls -l /home/mysqlsh/
total 43352
-rw-r----- 1 root root    1860 Mar 26 17:26 @.done.json
-rw-r----- 1 root root     706 Mar 26 17:25 @.json
-rw-r----- 1 root root     240 Mar 26 17:25 @.post.sql
-rw-r----- 1 root root     240 Mar 26 17:25 @.sql
-rw-r----- 1 root root   10281 Mar 26 18:55 load-progress.4319c033-5d90-11ed-b5da-0242ac140004.json
-rw-r----- 1 root root     488 Mar 26 17:25 world_x.json
-rw-r----- 1 root root     569 Mar 26 17:25 world_x.sql
-rw-r----- 1 root root     673 Mar 26 17:25 world_x@city.json
-rw-r----- 1 root root     828 Mar 26 17:25 world_x@city.sql
-rw-r----- 1 root root 1537747 Mar 26 17:25 world_x@city@0.tsv.zst
-rw-r----- 1 root root     224 Mar 26 17:25 world_x@city@0.tsv.zst.idx
-rw-r----- 1 root root 1466007 Mar 26 17:25 world_x@city@1.tsv.zst
-rw-r----- 1 root root     224 Mar 26 17:25 world_x@city@1.tsv.zst.idx
-rw-r----- 1 root root 1412253 Mar 26 17:25 world_x@city@10.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:25 world_x@city@10.tsv.zst.idx
-rw-r----- 1 root root 1416338 Mar 26 17:25 world_x@city@11.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:25 world_x@city@11.tsv.zst.idx
-rw-r----- 1 root root 1420107 Mar 26 17:26 world_x@city@12.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@12.tsv.zst.idx
-rw-r----- 1 root root 1413564 Mar 26 17:26 world_x@city@13.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@13.tsv.zst.idx
-rw-r----- 1 root root 1419191 Mar 26 17:26 world_x@city@14.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@14.tsv.zst.idx
-rw-r----- 1 root root 1371651 Mar 26 17:26 world_x@city@15.tsv.zst
-rw-r----- 1 root root     224 Mar 26 17:26 world_x@city@15.tsv.zst.idx
-rw-r----- 1 root root 1418129 Mar 26 17:26 world_x@city@16.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@16.tsv.zst.idx
-rw-r----- 1 root root 1418740 Mar 26 17:26 world_x@city@17.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@17.tsv.zst.idx
-rw-r----- 1 root root 1473313 Mar 26 17:26 world_x@city@18.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@18.tsv.zst.idx
-rw-r----- 1 root root 1415471 Mar 26 17:26 world_x@city@19.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@19.tsv.zst.idx
-rw-r----- 1 root root 1414672 Mar 26 17:25 world_x@city@2.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:25 world_x@city@2.tsv.zst.idx
-rw-r----- 1 root root 1423075 Mar 26 17:26 world_x@city@20.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@20.tsv.zst.idx
-rw-r----- 1 root root 1416003 Mar 26 17:26 world_x@city@21.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@21.tsv.zst.idx
-rw-r----- 1 root root 1422766 Mar 26 17:26 world_x@city@22.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@22.tsv.zst.idx
-rw-r----- 1 root root 1417149 Mar 26 17:26 world_x@city@23.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@23.tsv.zst.idx
-rw-r----- 1 root root 1416544 Mar 26 17:26 world_x@city@24.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@24.tsv.zst.idx
-rw-r----- 1 root root 1416591 Mar 26 17:26 world_x@city@25.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@25.tsv.zst.idx
-rw-r----- 1 root root 1413573 Mar 26 17:26 world_x@city@26.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@26.tsv.zst.idx
-rw-r----- 1 root root 1422132 Mar 26 17:26 world_x@city@27.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@27.tsv.zst.idx
-rw-r----- 1 root root 1414476 Mar 26 17:26 world_x@city@28.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@28.tsv.zst.idx
-rw-r----- 1 root root 1418757 Mar 26 17:26 world_x@city@29.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@29.tsv.zst.idx
-rw-r----- 1 root root 1409669 Mar 26 17:25 world_x@city@3.tsv.zst
-rw-r----- 1 root root     224 Mar 26 17:25 world_x@city@3.tsv.zst.idx
-rw-r----- 1 root root 1413113 Mar 26 17:26 world_x@city@30.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:26 world_x@city@30.tsv.zst.idx
-rw-r----- 1 root root 1414840 Mar 26 17:25 world_x@city@4.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:25 world_x@city@4.tsv.zst.idx
-rw-r----- 1 root root 1417435 Mar 26 17:25 world_x@city@5.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:25 world_x@city@5.tsv.zst.idx
-rw-r----- 1 root root 1415651 Mar 26 17:25 world_x@city@6.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:25 world_x@city@6.tsv.zst.idx
-rw-r----- 1 root root 1398405 Mar 26 17:25 world_x@city@7.tsv.zst
-rw-r----- 1 root root     224 Mar 26 17:25 world_x@city@7.tsv.zst.idx
-rw-r----- 1 root root 1411859 Mar 26 17:25 world_x@city@8.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:25 world_x@city@8.tsv.zst.idx
-rw-r----- 1 root root 1423065 Mar 26 17:25 world_x@city@9.tsv.zst
-rw-r----- 1 root root     232 Mar 26 17:25 world_x@city@9.tsv.zst.idx
-rw-r----- 1 root root     494 Mar 26 17:26 world_x@city@@31.tsv.zst
-rw-r----- 1 root root       8 Mar 26 17:26 world_x@city@@31.tsv.zst.idx
-rw-r----- 1 root root     653 Mar 26 17:25 world_x@country.json
-rw-r----- 1 root root     770 Mar 26 17:25 world_x@country.sql
-rw-r----- 1 root root    3669 Mar 26 17:26 world_x@country@@0.tsv.zst
-rw-r----- 1 root root       8 Mar 26 17:26 world_x@country@@0.tsv.zst.idx
-rw-r----- 1 root root     591 Mar 26 17:25 world_x@countryinfo.json
-rw-r----- 1 root root     854 Mar 26 17:25 world_x@countryinfo.sql
-rw-r----- 1 root root   12926 Mar 26 17:26 world_x@countryinfo@@0.tsv.zst
-rw-r----- 1 root root       8 Mar 26 17:26 world_x@countryinfo@@0.tsv.zst.idx
-rw-r----- 1 root root     707 Mar 26 17:25 world_x@countrylanguage.json
-rw-r----- 1 root root     991 Mar 26 17:25 world_x@countrylanguage.sql
-rw-r----- 1 root root    8697 Mar 26 17:26 world_x@countrylanguage@@0.tsv.zst
-rw-r----- 1 root root       8 Mar 26 17:26 world_x@countrylanguage@@0.tsv.zst.idx

ついでにファイル容量調べた

検証しててふと気づいた差分についてです。

ダンプしたファイル容量がかなり違ってました。

「mysqldump」と「mysqlpump」はほぼ同じですが、「ダンプロードユーティリティ」はかなり少ないですね。

bash-4.4#  du -ah -d 1 /home/
1.1G    /home/world_x_mysqlpump.sql
1.1G    /home/world_x_mysqldump.sql
43M     /home/mysqlsh
2.2G    /home/

まとめ

それぞれの結果を貼り付けているので表記に揺れがあります。

また、1秒未満は切り捨ててます。

方法 ダンプ時間 ロード時間 合計時間
mysqldump 0m23.256s 4m27.495s 4m50s
mysqlpump 0m19.934s 15m29.967s 15m48s
ダンプロードユーティリティ 00:00:07s 4 min 57 sec 5m4s

mysqlpumpのリストアが思いの外早くなかったですね。

ダンプロードユーティリティはダンプはめちゃくちゃ早いなってなりました。

ロードがあまり早くなかったので時と場合によって適している時があるのかなと思いました。

使った感じダンプロードユーティリティが一番近代的だなって感じでした。

ただ、制約もあるみたいなので注意が必要です。

参考資料

MySQL :: MySQL Shell 8.0 :: 11.6 Dump Loading Utility

MySQL Shellのユーティリティをコマンドラインで実行する - MicroAd Developers Blog

MySQL :: MySQL 8.0 リファレンスマニュアル :: 13.2.7 LOAD DATA ステートメント

MySQL :: MySQL 8.0 リファレンスマニュアル :: 5.1.8 サーバーシステム変数