なめらかなデータベース変更のためのガチョウマイグレーション

こんにちは、仲間!

今日は、データベースのマイグレーションとは何か、そしてそれがなぜ重要なのかについて話しましょう。今日の世界では、データベースに対する変更は慎重に行われ、特定のプロセスに従うべきだということは驚くべきことではありません。理想的には、これらのステップはCI/CDパイプラインに統合され、自動的にすべてが実行されるべきです。

アジェンダは以下の通りです:

  1. 問題は何か?
  2. どうやって解決するか?
  3. 簡単な例
  4. より複雑な例
  5. 推奨事項
  6. 結果
  7. 結論

問題は何か?

もしあなたのチームがデータベースのマイグレーションに対処したことがなく、なぜそれが必要なのか確信が持てない場合、整理してみましょう。基本を知っている場合は、先に進んでください。

主な課題

「計画的」で「スムーズな」データベース変更を行う際には、サービスの可用性を維持し、SLA要件を満たす必要があります(ユーザーがダウンタイムや遅延に苦しむことがないように)。500万人のユーザーを持つテーブルの列のタイプを変更したいと想像してみてください。これを「正面から」行うと(例えば、準備なしに単純にALTER TABLEを実行する)、テーブルがかなりの長時間ロックされる可能性があり、ユーザーはサービスなしに放置されてしまいます。

そのような頭痛を避けるために、次の2つのルールに従ってください:

  1. テーブルをロックしない方法でマイグレーションを適用する(または少なくともロックを最小限に抑える)。
  2. 列の型を変更する必要がある場合、最初に正しい型の新しい列を作成し、その後古い列を削除する方が簡単なことがよくあります。

別の問題:バージョン管理とロールバック

時にはマイグレーションをロールバックする必要があります。

これを手動で行うこと—本番データベースにアクセスしてデータをいじること—は、リスクが高いだけでなく、直接アクセスできない場合はほぼ不可能です。そこで、専用のマイグレーションツールが役立ちます。これらは、変更をクリーンに適用し、必要に応じて元に戻すことを可能にします。

どうやって修正するのか?適切なツールを使用する

各言語とエコシステムには独自のマイグレーションツールがあります:

  • Javaの場合、LiquibaseFlywayが一般的です。
  • Goの場合、人気の選択肢はgoose(ここで見ていくもの)です。
  • などなど。

Goose:それは何で、なぜ役立つのか

Gooseは、マイグレーションを自動的に管理するための軽量なGoユーティリティです。それは次のことを提供します:

  • シンプルさ。最小限の依存関係とマイグレーションのための透明なファイル構造。
  • 多用途性。さまざまなDBドライバー(PostgreSQL、MySQL、SQLiteなど)をサポートします。
  • 柔軟性。SQLまたはGoコードでマイグレーションを書くことができます。

Gooseのインストール

Shell

 

動作の仕組み:マイグレーション構造

デフォルトでは、Gooseはdb/migrations内のマイグレーションファイルを探します。各マイグレーションはこの形式に従います:

Shell

 

  • NNNはマイグレーション番号(例:001002など)です。
  • その後、任意の説明的な名前を付けることができます。例えばinit_schemaのように。
  • 拡張子は.sqlまたは.goにできます。

SQLマイグレーションの例

ファイル:001_init_schema.sql

SQL

 

最初の例

カラムタイプの変更(String → Int)

ここでは、usersテーブルにageというカラムがあり、そのタイプがVARCHAR(255)であるとします。これをINTEGERに変更したいと考えています。マイグレーションは次のようになります(ファイル005_change_column_type.sql):

SQL

 

ここで何が起こっているか:

  1. アップマイグレーション

    • ageカラムをINTEGERに変更します。USING (age::INTEGER)句は、PostgreSQLに既存のデータを新しい型に変換する方法を指示します。
    • このマイグレーションは、ageに数値でないデータが含まれている場合に失敗します。その場合、より複雑な戦略が必要になります(下記参照)。
  2. ダウンマイグレーション

    • ロールバックすると、ageVARCHAR(255) に戻します。
    • 再び USING (age::TEXT) を使用して、INTEGER からテキストに戻します。

第二の複雑なケース:マルチステップマイグレーション

もし age 列に雑なデータ(数字だけでなく)が含まれている可能性がある場合、いくつかのステップで行う方が安全です:

  1. 新しい列(age_int)を INTEGER 型で追加します。
  2. 有効なデータを新しい列にコピーし、無効なエントリを処理または削除します。
  3. 古い列を削除します。
SQL

 

適切なロールバックを可能にするために、Down セクションは逆のアクションを反映します。

自動化が鍵です

時間を節約するために、マイグレーションコマンドを Makefile(または他のビルドシステム)に追加するのが非常に便利です。以下は、PostgreSQL のための主要な Goose コマンドを含む Makefile の例です。

仮定しましょう:

  • データベースの DSN は postgres://user:password@localhost:5432/dbname?sslmode=disable です。
  • 移行ファイルはdb/migrationsにあります。
Shell

 

使い方は?

1. 新しい移行(SQLファイル)を作成する。これによりdb/migrations/002_add_orders_table.sqlというファイルが生成されます。

Shell

 

2. すべての移行を適用する。Gooseはデータベースにschema_migrationsテーブルを作成し(存在しない場合)、昇順で新しい移行を適用します。

Shell

 

3. 最後の移行をロールバックする。最後の移行を元に戻します。

Shell

 

4. すべての移行をロールバックする(本番環境では注意してください)。完全なリセット。

Shell

 

5. 移行のステータスを確認する

Shell

 

出力例:

Shell

 

概要

移行ツールとMakefileを使用することで、

  1. 本番データベースへの直接アクセスを制限し、変更を移行のみで行います。
  2. データベースのバージョンを簡単に追跡し、問題が発生した場合にそれらを元に戻すことができます。
  3. データベースの変更の一貫した履歴を維持します。
  4. マイクロサービスの世界で実行中の本番環境を壊さない「スムーズな」移行を行います。
  5. 追加の検証を取得します — すべての変更はPRとコードレビュープロセスを経ることになります(それらの設定がある場合を想定しています)。

また、これらすべてのコマンドをCI/CDパイプラインに簡単に統合することができる利点があります。そして覚えておくべきこと — 何よりもセキュリティが重要です。

例えば:

YAML

 

結論とヒント

主なアイデアは非常にシンプルです:

  • マイグレーションは小規模で頻繁に行いましょう。レビュー、テスト、必要に応じて元に戻すのが簡単です。
  • すべての環境で同じツールを使用しましょう。これにより、開発、ステージ、プロダクションが同期します。
  • マイグレーションをCI/CDに統合しましょう。そうすれば、特定の誰かに手動で実行してもらう必要がなくなります。

このようにして、データベース構造を変更するための信頼性が高く制御されたプロセスを持つことができます — プロダクションを壊さず、何か問題が発生した場合に迅速に対応できるプロセスです。

マイグレーションがうまくいくことを祈っています!

お読みいただきありがとうございます!

Source:
https://dzone.com/articles/goose-as-crucial-tool-for-your-service