本番のDBにマイグレーションを流す前に手を止めて、実行計画とバックアップの控えを見比べている一人運用の保守担当者

DBマイグレーションを本番へ当てる手順|一人運用で戻せる形に

「このテーブルに列を1本足しておいて」

言われたときは、ほんの数分の作業に見えます。開発環境ではコマンド1つで一瞬で終わる。ところが本番で流した瞬間、画面がぐるぐる回り始めて、問い合わせフォームが返ってこなくなる——。

DBのマイグレーションが怖いのは、難しいからではありません。コードと違って「さっきの状態に戻す」が簡単ではないからです。ファイルなら前のバージョンに差し替えれば済みますが、消したカラムの中身は戻ってこないし、動き出した本番のデータは1分ごとに増えていきます。

しかも一人運用だと、流している最中に誰かに見てもらうことができません。手が止まった瞬間に判断するのも、戻すのも、説明するのも全部自分です。緊張して当然の作業だと思います。

この記事では、DBのマイグレーションを本番へ当てるまでの段取りを、流す前・流すとき・流したあとの順に一緒に整理していきます。すべてを一度に整える必要はありません。まずは「戻せる形にしてから流す」という一点だけでも、だいぶ景色が変わります。

結論:DBマイグレーションを安全に当てる肝は3つです。①流す前に、実行時間とロックの見積もりを取っておく(本番と同じくらいのデータ量の検証環境で、実際に時間を測る)。②消す・変えるを、同じリリースでやらない(足す→両対応→切替→消す、の順に分けて、いつでも1つ前に戻れる状態を保つ)。③戻し方を、SQLの形で先に書いておく(「たぶん戻せる」ではなく、実行できる文として手元に置く)。この3つがそろっていれば、たとえ途中で止めることになっても、被害は「今日のリリースが延びる」だけで済みます。
使っているDB製品(MySQL / MariaDB / PostgreSQL など)とバージョン、マイグレーションツール(Flyway・Liquibase・Rails・Laravel・Alembic・Prisma・golang-migrate など)によって、挙動も安全な書き方も変わります。この記事の考え方を出発点に、必ずご自身の環境の公式ドキュメントで裏を取ってから本番へ当ててください。

なぜDBの変更だけ、こんなに神経を使うのか

コードのリリースには慣れているのに、DBだけ手が重い。それには、はっきりした理由があります。

どれも、注意力でどうにかなるものではありません。段取りで避けるしかない性質のものです。ここが分かっているだけで、「自分が下手だから怖い」という気持ちは少し軽くなると思います。

手順1:流す前に、実行時間とロックを見積もる

いちばん効くのに、いちばん飛ばされがちなのがここです。5分でできることから並べます。

まず、対象テーブルの行数とサイズを見る

本番で SELECT COUNT(*) を叩くのが重い場合は、統計情報から概算を見る方法があります(MySQL なら information_schema.tablesTABLE_ROWS、PostgreSQL なら pg_classreltuples)。桁が分かれば十分です。数千行なのか、数千万行なのか。ここで判断の重さが変わります。

検証環境で、本番に近い量を入れて時間を測る

これが一番の情報源です。本番のダンプがそのまま使えるなら理想ですが、個人情報の扱いがあるなら、行数だけ本番に合わせたダミーデータで構いません。知りたいのは中身ではなく「何分かかるか」です。

測ったら、その数字をリリース手順書に書いておきます。「約3分」と分かっているのと、「たぶんすぐ終わる」とでは、当日の落ち着きがまったく違います。

その変更が、テーブルを作り替えるタイプかを確かめる

同じ ALTER TABLE でも、その場で書き換わるものと、テーブルをまるごと作り直すものがあります。作り直すタイプは、行数に比例して時間もディスクも食います。

ロックで詰まったときに、自分から諦める設定を入れておく

これは、覚えておくと本当に助かる一手です。長時間のロック待ちを無制限に続けさせず、一定時間で自分から失敗させる設定を、マイグレーションの実行時だけ入れておきます(MySQL なら lock_wait_timeout、PostgreSQL なら lock_timeout)。

一見「失敗しやすくなる」ようですが、逆です。待ち行列が伸びてサービス全体が固まるより、そのマイグレーションだけが失敗して戻ってくるほうが、ずっと軽い傷で済みます。落ち着いた時間に、もう一度流せばいいだけです。

手順2:消す・変えるを、同じリリースでやらない

DBマイグレーションを、列を足す・両対応・切替・古い列を消す、の4段階に分けて横一列に並べた図
破壊的な変更ほど後ろへ。各段階のあいだで、いつでも1つ前に戻れる状態を保つ

DBの変更でいちばん事故が重くなるのは、アプリのコードとテーブルの形を、同時に取り替えようとしたときです。どちらかがほんの少しでも先行すると、そこでエラーが出ます。

これを避ける定番の考え方が、変更を「足す」から始めて「消す」で終わらせるやり方です。カラム名を tel から phone_number に変えたい、という例で見てみます。

  1. 足す:新しいカラム phone_number を追加する。このリリースでは、まだ誰も使いません。既存のコードは何ひとつ影響を受けないので、いちばん安全な変更です。うまくいかなければ、追加したカラムを消せば元通りになります。
  2. 両対応:アプリを、新旧どちらにも書き込む形に直してリリースする。読むのはまだ古いほう(tel)のまま。あわせて、既存データを新しいカラムへコピーする処理を流します(行数が多いなら、一度に全部ではなく数千行ずつ区切って流すと、ロックも負荷も軽くなります)。
  3. 切替:アプリの読み取り先を新しいカラムへ切り替える。書き込みは両方に残したままにしておきます。ここで問題が出ても、読み取りを古いほうに戻すだけで復旧できます。戻す手段が、コードのリリース1本で済むのがこの段階の良さです。
  4. 消す:数日〜数週間、問題が出ないことを確かめてから、古いカラム tel を落とす。ここまで来れば、消しても困る人はいません。

回りくどく見えるかもしれません。実際、手数は増えます。ただ、この形にしておくと、どの段階で止まっても「戻す」がやさしいのがいちばんの価値です。1〜3のどこで問題が起きても、データは失われません。

一人運用なら、1と2を同じ日に、3を翌日以降、4は月末の落ち着いたときに——くらいの分け方で十分回ります。全部を一気に終わらせなくて大丈夫です。

「足すだけ」に寄せるための小さな工夫

手順3:戻し方を、実行できるSQLとして先に書く

「まずければ戻せばいい」——この「戻す」が、頭の中にしかないことがよくあります。当日の慌てた頭で、正しい ALTER 文を組み立てるのは、思っている以上に難しい作業です。

だから、流すSQLを書いたその場で、戻すSQLもセットで書いてしまいます。マイグレーションツールを使っているなら、downrollback にあたる定義を空のままにしない、ということです。

そして、書いたら検証環境で、必ず一度は流して戻してみる。ここまでやって初めて「戻せる」と言えます。多くのツールで戻し方の定義は自動生成されますが、生成されたものが実際に流れるかどうかは別の話です。

トランザクションの効き方は、DB製品で違う

ここは事故につながりやすいところなので、少し丁寧に見ておきます。

MySQL系を使っているなら、1回のマイグレーションに変更を詰め込まないのが実務的な守りになります。5つの変更を1本にまとめると、3つ目で失敗したときに「2つだけ適用済み」という宙ぶらりんの状態が生まれます。1本ずつ分けておけば、どこまで進んだかが常にはっきりします。

バックアップは「取った」ではなく「戻せた」まで

流す直前のバックアップは、当然に取ります。ただ、ここでも一歩だけ踏み込んでおくと安心感が違います。

手順4:流すときと、流したあと

当日やることは、そう多くありません。

具体例:現場でよくある3つの場面

3つとも、技術力の問題ではありません。順番と、事前に測ったかどうかの違いです。

影響:段取りを型にすると、何が変わるか

明日やること:次のマイグレーションで、3つだけ

いきなり全部を型にしなくて大丈夫です。次の1回で足せる、小さな一歩を並べます。

  1. 対象テーブルの行数を調べて、手順書に1行書く(「約◯万行」で十分)。
  2. 検証環境で1回流して、時間を測る。ストップウォッチでも構いません。測った数字を手順書に書く。
  3. 戻すSQLを書いて、検証環境で流して戻してみる。ここまでできれば、当日やることはほとんど残っていません。
  4. 余裕があれば、止める基準を1行決める(「見積もりの2倍を超えたら中止」)。
  5. さらに余裕があれば、今回の変更に「消す」が含まれていないかを見て、含まれていたら次回以降に回せないか考える。

ここまでで、たぶん30分ほどです。それでも、本番で流す瞬間の心細さは、はっきり軽くなります。

「DBマイグレーションを本番へ当てる」チェックリスト

全部やる前提ではありません。一人運用で毎回すべては重いので、3段に分けてあります。まずは必須の3点だけ。行数の多いテーブルや、消す・変える系の変更のときに追加分を足してください。

【必須】どんな小さな変更でも、この3点だけは

【行数が多い・消す/変えるを含むなら追加】

【理想・任意】余裕があるときに

必須の3点なら、慣れれば30分ほどで回せます。全部に○が付かなくて大丈夫です。テーブルの大きさと、変更の種類に合わせて、必要な段まで足していけば十分です。

よければ、こちらも

DBのマイグレーションは、リリースの準備・戻し方・作業前バックアップと地続きの仕事です。あわせて整えておくと、本番へ当てる前後が1枚でつながります。

本番へのマイグレーションを無事に流し終えて、結果を確かめながらほっとしている一人運用の保守担当者

DBのマイグレーションで手が止まるのは、慎重すぎるからではありません。戻り道が用意されていないまま、前に進もうとしているからです。行数を測る、時間を測る、戻すSQLを書く。この3つがあるだけで、同じ作業が「引き返せる作業」に変わります。

今日は、次に当てる予定のテーブルの行数を1行だけ調べておく——それで十分です。その1行が、当日の指を、ずいぶん軽くしてくれます。

ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。

関連用語