この記事は羅針盤 アドベントカレンダー 2024の18日目の記事です。
qiita.com
16日目の記事は https://compasscorp.hatenablog.com/entry/isucon-2024:ISUCON 2024奮闘記 でした。
株式会社羅針盤の成田です。
今回は、私がmysqldumpというコマンドを覚えてから十数年使い続けている、秘伝の奥義を紹介します。
システム開発で途中参加したり引き継いだ場合、まずリバースエンジニアリングして既存のシステムを読み解くところから始まると思います。
「この操作をしたら、DBはどう変わるのだろう?」
という疑問に挑み続けていく戦いが始まります。
コードを読み解くにしても、隠されたコードがないか不安になります。
メタプログラミングやら何やらで黒魔術を使われていた時には発狂しそうになります。
そんな時、とある操作の前後のDBの状態を持っておいて比較すれば全てが解決します。
差分を出すことで、変更されたテーブルはそこだけ、それ以上でもそれ以下でもないという事実が得られます。
また、自分が追加した実装が本当に意図した通りのDB変更をしているか確認したい時にも使えます。
奥義の説明
1. リストア用のdumpファイル作成
ここでは "現在のDBの状態" をdumpしてファイルに保存します。
とある操作を何回か繰り返したい時や、ちょっと操作手順を変えて試したい時に、いつでもリストア(操作前にタイムスリップ)できるようにしておきます。
2. 何かしらの操作を行う前の状態のスナップショットを作成
ここでも "現在のDBの状態" をdumpしてファイルに保存します。
--skip-extended-insert
というオプションをつけて、INSERT文が1行ずつ出力されるようにしてdumpを作成します。
「1のdumpを使えばいいじゃないの?」と思いますが、skip-extended-insert
をつけていない1のリストア用dumpファイルは
insert into hoge values (レコード1), (レコード2), (レコード3), ...
という感じで、複数レコードがまとめられてしまい後述する差分比較ができなくなるためです。
「じゃあ2のdumpファイルだけで良いんじゃないの?」とも思いますが、1行ずつのdumpファイルはリストアに時間がかかります。
量にもよりますが、何十倍もの時間差が出てきます。 少量のデータベースであればそれでも差し支えありません。
3. 何かしらの操作
ここでシステムを動かしてみます。 試してみたい操作をしてみましょう。
4. 何かしらの操作を行った後の状態のスナップショットを作成
2と同じくスナップショットを作成します。
5. 差分を見る
操作の前後のファイル同士を比較し、何のテーブルが更新されたのか確認します
6. リストア
ログを仕込んで再度試したいとか、パラメータを変えて操作を再度試したい時には1のファイルを使ってリストアをします。 何をするにも自由です。
だっていつでもあの頃に戻れるのだから。
奥義の実践
サンプルDB・テーブル作成
create database example_db; create table reservations ( id bigint auto_increment primary key, date datetime );
1. リストア用のdumpファイル作成
mysqldump -h 127.0.0.1 -u xxx -pxxx example_db > example_db.2024-12-03.before-extended-insert.dump
2. 何かしらの操作を行う前の状態のスナップショットを作成
mysqldump -h 127.0.0.1 -u xxx -pxxx example_db --skip-extended-insert --skip-dump-date > example_db.2024-12-03.1.dump
3. 何かしらの操作
insert into reservations values(null, now());
(何かしらの操作した結果、reservationsテーブルにレコードが1つ追加されたという状況)
4. 何かしらの操作を行った後の状態のスナップショットを作成
mysqldump -h 127.0.0.1 -u xxx -pxxx example_db --skip-extended-insert --skip-dump-date > example_db.2024-12-03.2.dump
5. 差分を見る
diff example_db.2024-12-03.1.dump example_db.2024-12-03.2.dump 23c23 < ) ENGINE=InnoDB DEFAULT CHARSET=latin1; --- > ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; 29a30 > INSERT INTO `reservations` VALUES (1,'2024-12-03 03:09:38');
AUTO_INCREMENTの初期値の差分がノイズとなっていますが、まあ無視しましょう
6. リストア
mysql -h 127.0.0.1 -u xxx -pxxx example_db < example_db.2024-12-03.before-extended-insert.dump