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

  • 36
    Like
  • 0
    Comment

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でも動きます。

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