Posted at

DBマイグレーションツール作った話

More than 1 year has passed since last update.

GoでDB(MySQL)マイグレーションツールを作ったので紹介です。

先月末に行われたDevFest Kansai 2016でも紹介させてもらいました。その時の資料がこちらです。


carpenterとは

DBマイグレーション、結構面倒臭くないですか?


  • up.sql、down.sqlみたいなものを書くのが面倒だったり

  • バージョンを跨いで適用する時に面倒だったり

  • 小さな修正が面倒だったり

これらの面倒を解消するために作ったのがcarpenterです。


4つのサブコマンド

carpenterはコマンドラインツールで、以下の4つのサブコマンドを持っています。


  • design

  • build

  • export

  • import


design

% carpenter -s db_name -d "user:password@tcp(127.0.0.1:3306)" design -s -p -d /path/to/json/dir

designコマンドは、対象のDBのテーブル構造をJSONで抜き出します。

-s オプションは、テーブルごとにファイルを分けて出力するオプション。

-p オプションは、JSONを整形して出力するオプションです。(付けない場合はワンラインで出力します)


build

% carpenter -s db_name -d "user:password@tcp(127.0.0.1:3306)" build -d /path/to/json/dir

buildコマンドは、対象のDBへdesignコマンドで出力したJSONを元に、各テーブルへその構造を適用します。

適用とは、


  • JSONにあってDBに無いテーブルは作成

  • JSONになくてDBにあるテーブルは削除

  • JSONにあってDBにあるテーブル


    • JSONにあってテーブルに無いカラムは追加

    • JSONになくてテーブルにあるカラムは削除

    • JSONにあってテーブルにあるカラムは差分があれば更新

    • JSONにあってテーブルに無いINDEXは追加

    • JSONになくてテーブルにあるINDEXは削除

    • JSONにあってテーブルにあるINDEXは差分があれば削除、追加



という動きをします。

グローバルオプションとして --dry-run を用意していて、これを付けることで実行されるであろうSQLを標準出力へ出力します。

% carpenter --dry-run -s db_name -d "user:password@tcp(127.0.0.1:3306)" build -d /path/to/json/dir


export

% carpenter -s db_name -d "user:password@tcp(127.0.0.1:3306)" export -r "^master_.*$" -d /path/to/csv/dir

exportコマンドは、対処のDBのテーブルデータをテーブルごとにCSVファイルとして抜き出します。

-r オプションで抜き出したいテーブルを正規表現で指定することが出来ます。


import

% carpenter -s db_name -d "user:password@tcp(127.0.0.1:3306)" import -d /path/to/csv/dir

importコマンドは、対象のDBへexportコマンドで出力したCSVを元に、各テーブルへそのデータを適用します。

適用とは、


  • CSVにあってテーブルに無いデータは作成

  • CSVになくてテーブルにあるデータは削除

  • CSVにあってテーブルにあるデータは差分があれば更新

という動きをします。

importコマンドにはいくつか制限があって、詳しくはREADMEに書いてますが、idというカラムが必須だったりします。


使い方

仕事ではいわゆるソシャゲを作っていて、DB構造やテストデータなどはプログラマが作ったり、ゲームのマスターデータはプランナーが作ったりといった環境です。

そういった環境で、以下のような使い方を想定して作りました。


  • ローカルの開発環境にて機能開発(DBへの変更はGUIツールなどで好きに変更)

  • designコマンドでテーブル構造をエクスポートしてコードと一緒にGitにコミット

  • JenkinsなどでQA環境へデプロイ(この時buildコマンドも実行)

  • QA環境にてプランナーがデータ調整

  • exportコマンドでテーブルデータをエクスポートしてGitにコミット

  • Jenkinsなどでステージング環境へデプロイ(この時、build,importコマンドも実行)

Gitのブランチの状態とDBの状態の差分管理をするという思想が割りと気に入っていて、まだ荒削りな感じはありますが今後もアップデート予定です。


元々は少し違った

実はこのcarpenterは二代目で、初代はCSVを適用する機能のみのツールでした。

当時はGoでAPIサーバーを書いていて、マイグレーションには naoina/migu を使っていました。

miguはGoのstructをテーブルに適用するツールで、コードを書くだけでDBマイグレーションが完了するみたいな感じで最高でした。

ただデータseedの機能は無いため、データもCSVを同期するツールを作ろうと思って出来たのが初代です。

そんな最高なDBマイグレーション自動化による運営をしていたんですが、ちょっと前に別プロジェクトへ異動となり状況が変わりました。

そこにはそもそもDBマイグレーションという概念は無く、温かみのある手作業かつPHP。そうなるとmiguが使えない。そういった状況で生み出したのが二代目carpenterでした。

二代目はMySQLでさえあれば使えるし、migu+初代carpenterの実績もあって社内の新規プロジェクトは大体使い始めています。

こういったツールを作ったりするのにGoは最適で、簡単に並列処理が書けるし、Windowsでも動きます。

ソースコードは公開しているので、良かったら見てやって下さい。