GA

2025/06/20

バイナリログのバックアップについて考える in 2025

rsync

シェルが必要(たとえば ~/.ssh/authorized_keys で mysqldの実効アカウントにログインできるとか)なので新規に採用は嫌な感じ。
それを飲めるなら候補の一つとしてアリな気はする。
帯域ネックなら -z で圧縮をかけるとCPUを使いつつ転送に圧縮をかけられるが、転送後のファイルは圧縮されていない状態で現れるので保管を考えると更に圧縮する必要があって手間は手間。 binlog_transaction_compression が有効なら最初から圧縮されているのでベター。
binlog暗号化をされている状態でも保管はできるが、暗号化されたバイナリログを読み出すためにはkeyringが必要で、mysqldを起動させないといけない。
mysqldを止めずに実行する前提なので、バイナリログの最後のイベント(今まさに書き込んでいるイベント)は壊れているかもしれない。ただmysqlbinlogでデコードする時にワーニングが出るだけなのでわかっていさえすれば大丈夫。デフォルトではファイルのタイムスタンプが一致すれば次回はスキップされる。その時アクティブだったバイナリログは大体先頭から転送し直しになる。
コマンドの実行間隔がRPOになるが、他にレプリカがあれば最新のデータはそいつから取り出せるのでPITRの文脈であれば数分~15分, 30分, 1時間くらいは許容範囲なのではないかしら。

$ rsync user@host:/var/lib/mysql/mysql-bin.* ./
$ ll
total 375940
-rw-r----- 1 yoku0825 yoku0825       381 Jun 20 09:37 bin.000009
-rw-r----- 1 yoku0825 yoku0825       569 Jun 20 09:37 bin.000010
-rw-r----- 1 yoku0825 yoku0825 269449054 Jun 20 09:38 bin.000011
-rw-r----- 1 yoku0825 yoku0825 115493747 Jun 20 09:38 bin.000012
-rw-r----- 1 yoku0825 yoku0825        52 Jun 20 09:38 bin.index

mysqlbinlog

俺の大好きなパターン。 REPLICATION SLAVE 権限が必要。 —read-from-remote-server—raw を組み合わせて使う。 --raw を使わない場合はデコード後のテキストファイルが手に入るが、この場合PITRの時の —stop-datetime などのオプションが使えなくなる = テキストエディタで編集しないといけないのでなるべくそのままのバイナリログの形が好み。

  • 日々の覚書: mysqlbinlogでバイナリログをバックアップするとか
  • yoku0825/binlog_stream_container: Docker image for streaming MySQL binlog backup.
    通信経路圧縮をするにはzlibを使った -C だったが、8.0.18とそれ以降では —compression-algorithms でzstdが指定可能。zstdの方が軽くて速い。これもbinlog圧縮がされているなら圧縮後の転送量&ファイルサイズで済む。
    rsyncのようなワイルドカード指定がないので1つずつバイナリログファイル名を指定するか、「このバイナリログ以降のすべてをストリームし続ける( = —stop-never )」の2択しかない。転送対象に指定したバイナリログは手元にあろうがなかろうが全部先頭から再転送になる。
    rsyncと違ってmysqldが落ちている間は使えない。基本的にファイルを全部読み切ってから終了(または、 --stop-never で次のバイナリログを待機)するので最後のイベントが無効ってことはない。mysqlbinlog側がプロセスダウンしない限りは。
    binlog暗号化されている場合、mysqlbinlogが保管したファイルは平文に戻っている。得られたバイナリログを別途暗号化するなりなんなりが必要。
    --stop-never を使っている場合はmysqlbinlogプロセスが常駐して ( レプリケーションのレプリカと同じように ) バイナリログを逐次受信するためRPOがすこぶる短いことが期待できる。プロセスダウンをちゃんと検知して必要なら再起動する必要はあるけれども。
    そのへん(プロセスが起動された時に手元の最後のバイナリログのみ先頭から取り直す、手元にバイナリログがないならサーバーにある最初のファイルから読み出す)をケアしたスクリプトがこんな感じ。
  • https://github.com/yoku0825/binlog_stream_container/blob/main/binlog_stream_wrapper.sh#L22-L53

( ´-`).oO( holded ってなんだ… held だろう…

$ export MYSQL_PWD=xx
$ for num in {9..12} ; do
> mysqlbinlog -h sandbox.oci.yoku0825.work -P 64080 --raw -ubinlog -R --compression-algorithms=zstd "$(printf "%06d" $num)"
> done

$ ll
total 375936
-rw-r----- 1 yoku0825 yoku0825       381 Jun 20 09:40 bin.000009
-rw-r----- 1 yoku0825 yoku0825       569 Jun 20 09:40 bin.000010
-rw-r----- 1 yoku0825 yoku0825 269449054 Jun 20 09:40 bin.000011
-rw-r----- 1 yoku0825 yoku0825 115493747 Jun 20 09:41 bin.000012

MySQL Shellのutil.dumpBinlogs

最近の新顔。MySQL Shell 9.2か何かの新機能で、 MySQL Shell 8.4には入っていない 。2025年6月現在ではInnovation ReleaseのMySQL Shellが必要。
対象になるバイナリログファイルを特定するために SHOW BINARY LOGS を実行するので REPLICATION CLIENT 権限、実際にバイナリログを吸い上げるために REPLICATION SLAVE 権限が必要。MySQLプロトコルを使うので mysqld がダウンしてるとダメ。
俺が詳しくないだけかも知れないけど通信経路の圧縮はなさそう? サーバー側のbinlog圧縮が効いてれば節約になるのは他のと同じ。コイツだけ、「転送してきたバイナリログを圧縮してファイルに落とす」ので別途の圧縮は不要( binlog_transaction_compression
初回のみ、「最初に取り始めるバイナリログファイル名」を指定する必要があるが、2回目以降は「初回のバックアップで読み切ったポジションをメタデータファイルに残す」ので勝手に差分取得が可能。

mysqlbinlog と違って常駐はできないのでrsyncと同じくRPOの制約がある。実行のたびに指定したディレクトリに日時を示したディレクトリができるっぽい。

$ mysqlsh -h sandbox.oci.yoku0825.work -P 64080 -ubinlog -- util dumpBinlogs ./ { --startFrom=bin.000009 }
$ ll
total 8
drwxr-x--- 2 yoku0825 yoku0825 4096 Jun 20 09:58 2025-06-20-00-56-58
-rw-r----- 1 yoku0825 yoku0825  457 Jun 20 09:56 @.binlog.info.json

$ ll
total 8
drwxr-x--- 2 yoku0825 yoku0825 4096 Jun 20 09:58 2025-06-20-00-56-58
-rw-r----- 1 yoku0825 yoku0825  457 Jun 20 09:56 @.binlog.info.json

$ ll 2025-06-20-00-56-58/
total 182440
-rw-r----- 1 yoku0825 yoku0825       251 Jun 20 09:58 @.binlog.done.json
-rw-r----- 1 yoku0825 yoku0825     31614 Jun 20 09:56 @.binlog.json
-rw-r----- 1 yoku0825 yoku0825       275 Jun 20 09:56 bin.000009.json
-rw-r----- 1 yoku0825 yoku0825       274 Jun 20 09:56 bin.000009.zst
-rw-r----- 1 yoku0825 yoku0825       278 Jun 20 09:56 bin.000010.json
-rw-r----- 1 yoku0825 yoku0825       342 Jun 20 09:56 bin.000010.zst
-rw-r----- 1 yoku0825 yoku0825       295 Jun 20 09:58 bin.000011.json
-rw-r----- 1 yoku0825 yoku0825 130723963 Jun 20 09:58 bin.000011.zst
-rw-r----- 1 yoku0825 yoku0825       324 Jun 20 09:57 bin.000012.json
-rw-r----- 1 yoku0825 yoku0825  56018567 Jun 20 09:57 bin.000012.zst

もう一度実行すると差分がまた日付のディレクトリに現れる。今度は startFrom の指定をする必要はない。バックアップ先ディレクトリで一度でもdumpBinlogsが実行されていれば勝手に途中から実行する。

$ mysqlsh -h sandbox.oci.yoku0825.work -P 64080 -ubinlog -- util dumpBinlogs ./
Starting from previous dump: /home/yoku0825/work/2025-06-20-00-56-58, created at: 2025-06-20 00:56:58 UTC
Starting from binary log file: bin.000012:115493970
Will finish at binary log file: bin.000012:121584248
Dumping 1 binlogs (6.09 MB of data) using 4 threads
100% (6.09 MB / 6.09 MB), 3.27 MB/s, 2.46 MB/s compressed, 1 / 1 binlogs done
Dump was written to: /home/yoku0825/work/2025-06-20-01-03-00
Total duration: 00:00:01s
Binlogs dumped: 1
GTID set dumped: 6f9ee6f0-1feb-11f0-a8f1-02001702f486:449-9081
Uncompressed data size: 6.09 MB
Compressed data size: 4.64 MB
Compression ratio: 1.3
Events written: 17267
Bytes written: 4.64 MB
Average uncompressed throughput: 3.35 MB/s
Average compressed throughput: 2.55 MB/s

$ ll
total 16
drwxr-x--- 2 yoku0825 yoku0825 4096 Jun 20 09:58 2025-06-20-00-56-58
drwxr-x--- 2 yoku0825 yoku0825 4096 Jun 20 10:03 2025-06-20-01-03-00
-rw-r----- 1 yoku0825 yoku0825  457 Jun 20 09:56 @.binlog.info.json

$ ll 2025-06-20-01-03-00/
total 4568
-rw-r----- 1 yoku0825 yoku0825      42 Jun 20 10:03 @.binlog.done.json
-rw-r----- 1 yoku0825 yoku0825   31480 Jun 20 10:03 @.binlog.json
-rw-r----- 1 yoku0825 yoku0825     355 Jun 20 10:03 bin.000012.json
-rw-r----- 1 yoku0825 yoku0825 4636447 Jun 20 10:03 bin.000012.zst

$ ll */bin.000012*.zst
-rw-r----- 1 yoku0825 yoku0825 56018567 Jun 20 09:57 2025-06-20-00-56-58/bin.000012.zst
-rw-r----- 1 yoku0825 yoku0825  4636447 Jun 20 10:03 2025-06-20-01-03-00/bin.000012.zst

ファイルも先頭から取り直しているわけではなかった。

各zstファイルを解凍するといつもの(デコード前の)バイナリログが出てくる。
util.dumpBinlogs で取ったバックアップは util.loadBinlogs でディレクトリを指定する形で一気に適用できる。一つ一つ解凍して mysqlbinlog | mysql とかする必要はない。

binlog暗号化に関する話は mysqlbinlog と一緒。binlog圧縮の解凍はクライアントの責務だけど、暗号化されたbinlogを適用可能に復号するのはMySQLサーバー側の責務なので、MySQLプロトコルを使うクライアントサーバー形式だとどうしてもこうなる ( なので gh-ost とかも一緒)

【2025/06/20 18:18】

ただし、


     MySQL  localhost:3306  JS > util.dumpBinlogs('/backup/binlog_mysqlsh')
    The 'gtid_mode' system variable on the source instance is set to 'OFF'. This utility requires GTID support to be enabled. (ArgumentError)で

でした。最近サンドボックスも全部gtid_mode=ON統一だから気が付かなかった…

意外と楽しかった。

0 件のコメント :

コメントを投稿