schemalexを使ったMySQLのマイグレーション

ATOM開発チームの上野です。 普段はGo言語を使ってAPIサーバやバッチ処理機構の実装などを担当しています。

今回はATOMのプロジェクトにおける RDB(MySQL)のスキーママイグレーションの運用手法をご紹介します。

スキーママイグレーション

MySQLのようなリレーショナルデータベースでは、サービスの開発が進んでいくごとに テーブルやカラムの情報を変更していくため、DDLを管理する必要があります。

一般的にはこのDDLの更新情報をマイグレーションツールを利用することになると思います。 マイグレーションの仕組みはツールによって異なりますが、多くは「マイグレーションファイル」と呼ばれる スキーマの変更情報を記録したファイル群を適用日時順に保存しておき、 それらを順次適用したり、遡るように実行することでスキーマの変更をロールバックしたりできます。

golang-migrate はGo言語の代表的なマイグレーションツールの一つであり、 ATOMプロジェクトで元々利用していたものですが、 上記のマイグレーションファイルを用いた形式であったため、以下のデメリットがありました

  • スキーマ変更が頻繁に行われるたびにマイグレーションファイルが量産されてしまう
    • 新しく環境を構築する際、1から全てのファイルを適用するので時間がかかってしまう
  • 「実際に適用するDDL」と「ロールバック用のDDL」を2つずつ用意しないといけないので面倒
  • (golang-migrationの場合) 最新適用時間よりも前のマイグレーションファイルは実行されない
    • 本来先にマージして適用されるべきマイグレーションファイルが後からマージされた場合、そのファイルは実行されないという問題が起きた

この問題を解消するため、新しく「git-schemalex」というマイグレーションツールを試験的に導入し、 1年以上運用してみたところ感触が良かったため、ご紹介します。

git-schemalex

git-schemalex はGo製のマイグレーションツールで、 gitのコミットハッシュと1つのスキーマファイルでマイグレーションを実現します。

git-schemalexを利用するには、まず以下のように、 最初のスキーマファイルを用意し、gitのリポジトリにコミットします。

CREATE TABLE `users` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT 'ユーザー名',
  `password` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT 'パスワード',
  `created_at` datetime NULL NOT NULL,
  `updated_at` datetime NULL NOT NULL,
  `deleted_flag` tinyint(3) unsigned NOT NULL,
  PRIMARY KEY (`id`)
)

次に、上記のファイルを指定し、git-schemalex を実行すると 指定したDB上にスキーマファイルと通りにusersテーブルが作成されます。

$ go get github.com/schemalex/git-schemalex/cmd/git-schemalex
$ git schemalex -schema path/to/schema.sql -dsn "$root:$passowrd@/$database" -deploy

# -> CREATE TABLEが実行される

その後、usersテーブルに以下の変更を入れてみます。

  • password カラムの長さを 20 から 50 に変更
  • deleted_flagdeleted_at(datetime) に変更
  • name カラムにユニークキーを追加

差分は以下のようになります。

CREATE TABLE `users` (
   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
   `name` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT 'ユーザー名',
-  `password` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT 'パスワード',
+  `password` varchar(50) COLLATE utf8mb4_bin NOT NULL COMMENT 'パスワード',
   `created_at` datetime NULL NOT NULL,
   `updated_at` datetime NULL NOT NULL,
-  `deleted_flag` tinyint(3) unsigned NOT NULL,
-  PRIMARY KEY (`id`)
+  `deleted_at` datetime NULL DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `unique_key_name` (`name`)
)

この差分をコミットし、git-schemalexを実行すると 差分をパースして以下のDDLを自動生成、適用してくれます

$ git schemalex -schema path/to/schema.sql -dsn "$root:$passowrd@/$database" -deploy
ALTER TABLE `users` CHANGE COLUMN `password` `password` VARCHAR (50) COLLATE `utf8mb4_bin` NOT NULL COMMENT 'パスワード';

ALTER TABLE `users` DROP COLUMN `deleted_flag`;

ALTER TABLE `users` ADD COLUMN `deleted_at` DATETIME DEFAULT NULL AFTER `updated_at`;

ALTER TABLE `users` ADD UNIQUE INDEX `unique_key_name` (`name`);

UPDATE git_schemalex SET version = ?;[9d327974493b3db292986691104e62fbb833923e]

git-schemalexはスキーマ変更を適用時、事前に用意されたテーブルにコミットハッシュを保存し、 次回の差分と比較できるようにします。 (発行クエリの最後UPDATE文がそれに該当します)

実運用の際は、上記のスキーマファイル1つをgit管理し、 リリース用ブランチをマージすると同時にDBに適用するフローを組むことになります。

git-schemalexの特徴

マイグレーションファイルをバージョン数分管理する方式に比べ、 git-schemalexは以下の特徴があります。

[メリット]

  • 1つのスキーマファイルだけ管理すれば良いので、変更も管理も楽
  • ALTER文を自力で書く必要がないため、ミスが減る、レビューコストも下がる

[デメリット]

  • MySQLしか対応していない
    • 追加開発も特に行われていない様子
  • 意図しないDDLが発行されないように、差分に気をつける必要がある
    • うっかり行を削ってしまうと、当然カラムが落とされてしまう

本格導入する時は勇気が要りましたが、実際に運用してみると 想像以上にスキーマ変更が楽になり、使い始めると手放せないものになりました。 特に開発スピードが早く、スキーマの変更が目まぐるしいプロジェクトだと 導入することでみんなが幸せになれるかもしれません。

まとめ

今回はATOMプロジェクトのDBスキーママイグレーション手法をご紹介しました。 マイグレーションの運用はどのプロジェクトでも悩むポイントだと思いますので、 ぜひ参考にしていただければと思います!