
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だけ手が重い。それには、はっきりした理由があります。
- 戻す操作が対称ではない。
ADD COLUMNの反対はDROP COLUMNですが、これは「戻す」ではなく「消す」です。落としたカラムに入っていた値は、バックアップから拾い直すしかありません。 - 流している最中もサービスは動いている。ファイルの差し替えは一瞬ですが、テーブルの作り替えは分単位・時間単位になることがあります。その間ずっと、お客さんはそのテーブルを使い続けています。
- ロック(他の処理を待たせる仕組み)が、思わぬ広さで効くことがある。1本の
ALTER TABLEが、そのテーブルを読むだけの処理まで待たせてしまい、待ちが積み上がってアプリ側のコネクションが枯れる。画面が固まる原因の多くはこれです。 - 開発環境では絶対に再現しない。手元のテーブルが100行で、本番が2000万行なら、体感はまったく別物になります。「一瞬で終わった」は、本番の予測にはなりません。
- アプリのコードとDBの状態が、一瞬ずれる。新しいコードが古いテーブルを見る/古いコードが新しいテーブルを見る。この数十秒のずれで、エラーが出ます。
どれも、注意力でどうにかなるものではありません。段取りで避けるしかない性質のものです。ここが分かっているだけで、「自分が下手だから怖い」という気持ちは少し軽くなると思います。
手順1:流す前に、実行時間とロックを見積もる
いちばん効くのに、いちばん飛ばされがちなのがここです。5分でできることから並べます。
まず、対象テーブルの行数とサイズを見る
本番で SELECT COUNT(*) を叩くのが重い場合は、統計情報から概算を見る方法があります(MySQL なら information_schema.tables の TABLE_ROWS、PostgreSQL なら pg_class の reltuples)。桁が分かれば十分です。数千行なのか、数千万行なのか。ここで判断の重さが変わります。
検証環境で、本番に近い量を入れて時間を測る
これが一番の情報源です。本番のダンプがそのまま使えるなら理想ですが、個人情報の扱いがあるなら、行数だけ本番に合わせたダミーデータで構いません。知りたいのは中身ではなく「何分かかるか」です。
測ったら、その数字をリリース手順書に書いておきます。「約3分」と分かっているのと、「たぶんすぐ終わる」とでは、当日の落ち着きがまったく違います。
その変更が、テーブルを作り替えるタイプかを確かめる
同じ ALTER TABLE でも、その場で書き換わるものと、テーブルをまるごと作り直すものがあります。作り直すタイプは、行数に比例して時間もディスクも食います。
- MySQL(InnoDB):多くの操作をサービスを止めずに行える「オンラインDDL」の仕組みがあり、操作ごとに
INSTANT/INPLACE/COPYのどれになるかが決まっています。ALTER TABLE ... , ALGORITHM=INSTANT;のように明示して実行し、エラーになるかどうかで確かめるのが確実です(対応していなければエラーで止まるので、本番を巻き込まずに判定できます)。どの操作がどれに当たるかは公式の一覧が正確です。 - PostgreSQL:バージョン11以降、デフォルト値つきの列追加はテーブル全体の書き換えなしで済むようになっています(関数の種類によっては書き換えが起きます)。一方で、型の変更などはテーブルの書き換えを伴います。
- インデックス追加:PostgreSQL には
CREATE INDEX CONCURRENTLY(書き込みを止めずに作る)という選択肢があります。ただし通常より時間がかかり、トランザクションの中では使えず、失敗すると無効なインデックスが残るので、後始末まで含めて手順にしておきます。
ロックで詰まったときに、自分から諦める設定を入れておく
これは、覚えておくと本当に助かる一手です。長時間のロック待ちを無制限に続けさせず、一定時間で自分から失敗させる設定を、マイグレーションの実行時だけ入れておきます(MySQL なら lock_wait_timeout、PostgreSQL なら lock_timeout)。
一見「失敗しやすくなる」ようですが、逆です。待ち行列が伸びてサービス全体が固まるより、そのマイグレーションだけが失敗して戻ってくるほうが、ずっと軽い傷で済みます。落ち着いた時間に、もう一度流せばいいだけです。
手順2:消す・変えるを、同じリリースでやらない

DBの変更でいちばん事故が重くなるのは、アプリのコードとテーブルの形を、同時に取り替えようとしたときです。どちらかがほんの少しでも先行すると、そこでエラーが出ます。
これを避ける定番の考え方が、変更を「足す」から始めて「消す」で終わらせるやり方です。カラム名を tel から phone_number に変えたい、という例で見てみます。
- 足す:新しいカラム
phone_numberを追加する。このリリースでは、まだ誰も使いません。既存のコードは何ひとつ影響を受けないので、いちばん安全な変更です。うまくいかなければ、追加したカラムを消せば元通りになります。 - 両対応:アプリを、新旧どちらにも書き込む形に直してリリースする。読むのはまだ古いほう(
tel)のまま。あわせて、既存データを新しいカラムへコピーする処理を流します(行数が多いなら、一度に全部ではなく数千行ずつ区切って流すと、ロックも負荷も軽くなります)。 - 切替:アプリの読み取り先を新しいカラムへ切り替える。書き込みは両方に残したままにしておきます。ここで問題が出ても、読み取りを古いほうに戻すだけで復旧できます。戻す手段が、コードのリリース1本で済むのがこの段階の良さです。
- 消す:数日〜数週間、問題が出ないことを確かめてから、古いカラム
telを落とす。ここまで来れば、消しても困る人はいません。
回りくどく見えるかもしれません。実際、手数は増えます。ただ、この形にしておくと、どの段階で止まっても「戻す」がやさしいのがいちばんの価値です。1〜3のどこで問題が起きても、データは失われません。
一人運用なら、1と2を同じ日に、3を翌日以降、4は月末の落ち着いたときに——くらいの分け方で十分回ります。全部を一気に終わらせなくて大丈夫です。
「足すだけ」に寄せるための小さな工夫
- NOT NULL 制約は、あとから付ける。まず NULL を許す形で足し、データを埋め、そのあとで制約を付ける。最初から NOT NULL にしようとすると、既存行の扱いで詰まります。
- リネームは、リネームしない。「名前を変える」は、実質「足して・移して・消す」です。最初から3つの作業として考えると、段取りが自然に決まります。
- 列の削除は、まず使うのをやめるだけにする。アプリから参照しなくなってしばらく置いておけば、「実はここで使っていた」に気づく余地が残ります。
手順3:戻し方を、実行できるSQLとして先に書く
「まずければ戻せばいい」——この「戻す」が、頭の中にしかないことがよくあります。当日の慌てた頭で、正しい ALTER 文を組み立てるのは、思っている以上に難しい作業です。
だから、流すSQLを書いたその場で、戻すSQLもセットで書いてしまいます。マイグレーションツールを使っているなら、down や rollback にあたる定義を空のままにしない、ということです。
そして、書いたら検証環境で、必ず一度は流して戻してみる。ここまでやって初めて「戻せる」と言えます。多くのツールで戻し方の定義は自動生成されますが、生成されたものが実際に流れるかどうかは別の話です。
トランザクションの効き方は、DB製品で違う
ここは事故につながりやすいところなので、少し丁寧に見ておきます。
- PostgreSQL は、テーブルの定義変更(DDL)をトランザクションの中に入れられます。つまり、複数の変更をまとめて実行し、途中で失敗したら全部なかったことにできます(
CREATE INDEX CONCURRENTLYなど、一部の例外はあります)。 - MySQL は、DDLを実行するとその時点で暗黙のコミットが走ります。トランザクションで囲っても、まとめて巻き戻すことはできません。8.0以降の「アトミックDDL」は、そのDDL1本が中途半端な状態で終わらないことを保証する仕組みで、あとから自分の判断で戻せるという意味ではない点に注意します。
MySQL系を使っているなら、1回のマイグレーションに変更を詰め込まないのが実務的な守りになります。5つの変更を1本にまとめると、3つ目で失敗したときに「2つだけ適用済み」という宙ぶらりんの状態が生まれます。1本ずつ分けておけば、どこまで進んだかが常にはっきりします。
バックアップは「取った」ではなく「戻せた」まで
流す直前のバックアップは、当然に取ります。ただ、ここでも一歩だけ踏み込んでおくと安心感が違います。
- 対象テーブルだけの控えを取る。全体のダンプは時間がかかりますが、
mysqldumpでテーブルを指定する、CREATE TABLE ... AS SELECTで退避用のテーブルにコピーしておく、といった方法なら短時間で済みます。データを書き換える系のマイグレーションでは、これがいちばん速い保険になります。 - 戻せるかを、検証環境で一度確かめておく。復元のテストは、それだけで独立したテーマなので、詳しくは記事末尾のリストア記事にゆずります。ここでは「取っただけで安心しない」とだけ。
手順4:流すときと、流したあと
当日やることは、そう多くありません。
- 時間帯を選ぶ。アクセスの少ない時間に。ただし「深夜だから安全」ではなく、夜間バッチと重ならないかを先に確かめます。集計バッチと
ALTER TABLEがぶつかると、両方が長く待ち合います。 - 流す前に、もう一度だけ接続先を確認する。検証環境のつもりで本番につながっていた、は誰にでも起こります。プロンプトの表示やホスト名を、声に出して読むくらいで丁度いいと思います。
- 流している最中は、待ち状況を別の画面で見る。MySQL なら
SHOW PROCESSLIST、PostgreSQL ならpg_stat_activity。待たされている処理が増え始めていないかが見どころです。増えていたら、そこが判断の分かれ目になります。 - 止める判断を、先に決めておく。「見積もりの2倍の時間を超えたら中止する」「待ちが◯件を超えたら中止する」。数字で決めておけば、当日の自分は判断しなくていい——これが一人運用ではいちばん効きます。
- 終わったら、件数で確かめる。データを移したなら、移す前と後の件数を突き合わせる。
NULLが残っていないか、想定した値が入っているかを、代表的な数行だけ目で見る。 - アプリ側の動きも見る。DBが変わってエラーが出るのは、たいていアプリ側です。主要な導線を数本通し、エラーログが反映時刻から増えていないかを確認します。
具体例:現場でよくある3つの場面
- 列を1本足しただけで、サイトが固まった:2000万行のテーブルに
ALTER TABLE ... ADD COLUMNを流したところ、テーブルの作り替えが走って十数分かかり、その間の書き込みが詰まった。→ 検証環境で同じ行数を用意して時間を測っていれば、事前に気づけました。加えてALGORITHMを明示して流していれば、作り替えが必要な操作だと実行前にエラーで教えてくれた場面です。 - リリースの数十秒で、エラーが大量に出た:カラム名を変更し、アプリのコードも同時にリリースした。DBの変更が先に通り、コードの反映が遅れた数十秒のあいだ、古いコードが存在しないカラムを参照してエラーになった。→ 「足す→両対応→切替→消す」に分けていれば、この数十秒そのものが発生しません。
- 戻そうとしたら、戻せなかった:不要と判断したカラムを削除するマイグレーションを流したあと、別の画面がそのカラムを使っていたことが判明。
downの定義には「カラムを追加し直す」とだけ書いてあり、中のデータは戻らなかった。→ 削除を最後の段階に回し、しばらく置いてから消していれば、気づく余地がありました。
3つとも、技術力の問題ではありません。順番と、事前に測ったかどうかの違いです。
影響:段取りを型にすると、何が変わるか
- リリースの前の晩が、少し眠れるようになる。「まずくなったらここで止めて、こう戻す」が決まっていると、不安の輪郭がはっきりします。
- 中止できるようになる。戻し方が用意されていないと、人は途中でやめられません。引き返せる道があると、迷わず引き返せます。
- 説明が短くなる。「見積もりは3分、実測4分、件数一致、エラー増加なし」と記録が残っていれば、あとから何か起きたときの切り分けが速く進みます。
- DBの変更が、特別な行事でなくなる。手順が同じ形になるほど、頻度を上げても事故が増えなくなります。小さく何度もが、いちばん安全な当て方です。
明日やること:次のマイグレーションで、3つだけ
いきなり全部を型にしなくて大丈夫です。次の1回で足せる、小さな一歩を並べます。
- 対象テーブルの行数を調べて、手順書に1行書く(「約◯万行」で十分)。
- 検証環境で1回流して、時間を測る。ストップウォッチでも構いません。測った数字を手順書に書く。
- 戻すSQLを書いて、検証環境で流して戻してみる。ここまでできれば、当日やることはほとんど残っていません。
- 余裕があれば、止める基準を1行決める(「見積もりの2倍を超えたら中止」)。
- さらに余裕があれば、今回の変更に「消す」が含まれていないかを見て、含まれていたら次回以降に回せないか考える。
ここまでで、たぶん30分ほどです。それでも、本番で流す瞬間の心細さは、はっきり軽くなります。
「DBマイグレーションを本番へ当てる」チェックリスト
全部やる前提ではありません。一人運用で毎回すべては重いので、3段に分けてあります。まずは必須の3点だけ。行数の多いテーブルや、消す・変える系の変更のときに追加分を足してください。
【必須】どんな小さな変更でも、この3点だけは
- 対象テーブルの行数の桁を確かめたか(数千行か、数千万行か)
- 検証環境で一度流して、実行時間を測ったか
- 戻すSQLを書き、検証環境で実際に戻せることを確かめたか
【行数が多い・消す/変えるを含むなら追加】
- 変更がテーブルの作り替えを伴うかを、公式ドキュメントか
ALGORITHMの明示で確かめたか - ロック待ちの上限時間(
lock_wait_timeout/lock_timeout)を設定して流すようにしたか - 消す・名前を変える変更を、「足す→両対応→切替→消す」に分けたか
-
NOT NULLなどの制約を、データを埋めたあとに付ける順にしたか - 対象テーブルのバックアップ(または退避テーブル)を、流す直前に取ったか
- 1回のマイグレーションに変更を詰め込みすぎていないか(特にMySQL系)
- 夜間バッチや定期処理と重ならない時間帯を選んだか
- 中止する基準を数字で決めてあるか(時間・待ち件数)
- 流したあと、件数の突き合わせと主要導線の動作確認をしたか
- 実行日時・所要時間・結果を、変更管理台帳に1〜2行残したか
【理想・任意】余裕があるときに
- データの移行を、数千行ずつに区切って流す形にしたか
- 本番と同じ行数の検証用データを用意する手順を、使い回せる形で残したか
- 今回気になった点を1行だけ書き、次のマイグレーション手順に反映したか
必須の3点なら、慣れれば30分ほどで回せます。全部に○が付かなくて大丈夫です。テーブルの大きさと、変更の種類に合わせて、必要な段まで足していけば十分です。
よければ、こちらも
DBのマイグレーションは、リリースの準備・戻し方・作業前バックアップと地続きの仕事です。あわせて整えておくと、本番へ当てる前後が1枚でつながります。
- 本番反映前チェックリスト|作業前バックアップの取り方を一人運用で:流す直前のバックアップを、ファイル・DB・設定の種類別に具体化しています。
- ロールバック手順の作り方|すぐ戻せるリリース設計を一人運用で:「戻す」をアプリ・DB・設定それぞれでどう用意するかの土台です。
- 本番データをSQLで直すときの安全手順|バックアップと件数確認:データを書き換える側の作業は、こちらで手順を分けて整理しています。
- バックアップから戻せるか確かめる|復元(リストア)テストの手順:「取った」を「戻せた」に変えるための確認手順です。
- リリース後の確認項目|動作・監視・問い合わせを見る順番と時間:当てたあとに、何をいつ見るかをまとめています。
- 変更管理台帳の付け方|いつ・誰が・何を変えたかを残す:実行日時と所要時間を残しておくと、次の見積もりが楽になります。

DBのマイグレーションで手が止まるのは、慎重すぎるからではありません。戻り道が用意されていないまま、前に進もうとしているからです。行数を測る、時間を測る、戻すSQLを書く。この3つがあるだけで、同じ作業が「引き返せる作業」に変わります。
今日は、次に当てる予定のテーブルの行数を1行だけ調べておく——それで十分です。その1行が、当日の指を、ずいぶん軽くしてくれます。
ほかの実務ヒントは記事一覧からどうぞ。保守運用の小さな備えを、メールでも少しずつお届けしています。