Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DB移行、油断した瞬間に 肝が冷えた話

DB移行、油断した瞬間に 肝が冷えた話

JAWS-UG 神戸 #13 / 2026-07-30

Avatar for Shuma

Shuma

July 30, 2026

More Decks by Shuma

Other Decks in Programming

Transcript

  1. OVERVIEW 今日の話 = この移行 5.6 → 8.4 約230GB 74分 /

    3時間 MySQL 3メジャーバージョン飛ばし 本番データ量(EC2 c5.12xlarge → RDS) dump(21GB) / インポート実測 方式: mysql.rds_set_external_source によるbinlogレプリケーション 開発 手順の初回実施 → 同じ手順を3回まわして、本番はぶっつけにしない ステージング 本番リハーサル → 本番 2026-07-09 切替
  2. ARCHITECTURE (BEFORE) 移行前の構成 — DBまでぜんぶEC2 binlog レプリ 参照/更新 Internet ALB

    + WAF アプリEC2群 DBマスター DBスレーブ ドメイン別に複数系統 Web / 管理 / 決済 / バッチ EC2 / MySQL 5.6.50 EC2 / 別AZ SSL終端 nginx + PHP (Symfony) 48vCPU (AZ-a) 日次dump・調査 周辺コンポーネント(今回のスコープ外) ElastiCache (Valkey) DynamoDB S3 + Athena 監視EC2 キャッシュ セッション管理 ログ保管・分析 Prometheus / Grafana DBは監視・パッチ・フェイルオーバーまで全部自前。 ※ 実構成から固有情報を省略した概略図 今回このマスター/スレーブをRDS (Multi-AZ) に置き換える
  3. PREPARATION 1 / 3 スペックは実測で決めた EC2の自前DBはメトリクスが勝手に集まらない マスター/スレーブにSSHで入り、コマンドで実測 CPU 2〜3% 平均使用率(ピークでも

    4.7%) 設定の実態も棚卸し buffer pool は RAM 96GB 中 40GB の割当てなど 見積もりではなく実測で判断 ほぼ遊んでいる48vCPUを維持する理由がなかった 48 → 16 vCPU c5.12xlarge → db.m8g.4xlarge へ適正化
  4. PREPARATION 2 / 3 5秒の確認が半日を救った インポート直前、念のため確認 SHOW BINARY LOGS で控えていたレプリ開始位置を再確認

    5秒 かかった確認の時間 binlogはパージ済みで存在しなかった vs そのまま流せば数時間のインポート後にエラー1236で全やり直し dumpを即取り直して回避 重い処理の前の軽い確認、が身に染みた 半日 救われた手戻りの時間
  5. PREPARATION 3 / 3 事故は人の注意ではなく構造で防ぐ ステージングで意図しない二重書き込み 開発環境の .env が新RDSを向いたまま →

    PK重複(エラー1062)でレプリケーション停止 反省を本番に反映: 同期期間中は read_only=1 書き込みを物理的に遮断。「気をつける」ではなく「書けない」状態を作る ここまでやった。油断はしていなかった —— はずだった。
  6. 移行方式: binlogレプリケーションの流れ 1 スレーブからdump取得(74分 / 21GB) --dump-slave でレプリ開始位置を記録。本番マスターは無負荷のまま 2 中継EC2経由でRDSへインポート(約3時間)

    sedで照合順序を焼き込みつつ投入 3 mysql.rds_set_external_source でレプリケーション開始 RDSを旧マスターのレプリカにする(binlog位置指定) 4 同期を待つ(数時間で遅延0に収束) この間もサービスは旧環境で通常稼働。read_only=1で保護 5 アプリ切替 → レプリ解除・書込可能化(昇格) rds_reset_external_source → read_only=0。切替当日の作業はこれだけ
  7. ROOT CAUSE 原因は mysqldump の1オプション mysqldumpの既定文字コードは utf8mb3 エラー 0件 --default-character-set=utf8mb4

    の指定が漏れていた 警告もなし。インポートは正常終了 4バイト文字が「?」に置換されてダンプ 絵文字・一部の異体字など。3バイト以内の文字は無傷 5.6のデータ 「 🍺」 4バイト文字 → dump(utf8mb3) ここで欠落 → RDSには 「?」 サイレント破損
  8. IRONY 皮肉なことに 照合順序(collation) 文字コード(charset) • sedでダンプに明示COLLATEを焼き込み • その一段「隣」のレイヤーに穴 • 220テーブルを完全統一

    • 3バイト以内の文字は無傷 • 手を尽くして守り切った • → 抽出検証も切替後の目視もすり抜け サイレント破損は、検証設計の外側に落ちる
  9. RECOVERY 復旧 — 旧5.6を「正」として書き戻す 1 切替時点のデータを保持していた旧5.6を「正」に 止めてあった旧サーバーがそのまま正解データだった 37 / 219

    被害を受けたテーブル数 2 バイト一致する行だけ、カラム単位で書き戻し 切替後にユーザーが編集した行は上書きしない 約20時間 3 書き戻し残 0件まで照合して完了 発覚から全復旧まで 全テーブルを突き合わせて完了判定