web-dev-qa-db-ja.com

複数のアプリインスタンスでデータベースの移行を安全に実行するにはどうすればよいですか?

高速(1秒未満)と低速のデータベース移行(> 30秒)の両方が混在するアプリケーションがあります。現在、データベース移行をCIの一部として実行していますが、CIツールはアプリのデータベース接続文字列をすべて(複数の環境で)認識している必要があるため、理想的ではありません。このプロセスを変更して、アプリケーションが起動時に独自のデータベース移行を実行するようにします。

ここに状況があります:

このアプリケーションの複数のインスタンスがあり、約5つが本番環境にあります。それらを呼びましょうnode1, ..., node5。各アプリは単一のSQL Serverインスタンスに接続し、ローリングデプロイを使用していません(すべてのアプリは私の知る限り同時にデプロイされています)。

問題:長時間の移行があるとします。この場合、 node1開始し、次に移行の実行を開始します。さて、node4が開始され、長時間の移行がまだ完了していないため、node4も移行の実行を開始します->データ破損の可能性はありますか?この問題をどのように防止しますか、または問題は心配するほど重要ですか?

この問題を分散ロックで解決することを考えていました(etcdまたはそれらの行に沿った何かを使用して)。基本的に、すべてのアプリはロックを取得しようとしますが、そのうちの1つだけがロックを取得して移行を実行し、その後ロックを解除します。残りのアプリが起動してクリティカルセクションに入ると、すべての移行がすでに実行されているため、移行スクリプトは終了します。

しかし、私の直感は「これはやり過ぎです。もっと簡単な解決策があるはずです」と言っているので、他の誰かがもっと良いアイデアを持っているかどうかを確認するためにここに尋ねると思いました。

10
Ben

この以前のDBA.SEの投稿 によると、SQLサーバーについて言及したので、スキーマの変更をトランザクションに含めることができます(すべきです)。これにより、他の形式のDBへの同時書き込みと同様に、移行を設計できます。トランザクションを開始し、失敗した場合はロールバックします。これにより、少なくとも一部の最悪のデータベース破損シナリオが防止されます(ただし、列やテーブルの削除などの破壊的な移行手順がある場合、トランザクションだけではデータ損失は防止されません)。

これまでのところ、適用済みの移行が登録されているmigrationsテーブルも必要になると思います。これにより、アプリケーションプロセスは、特定の移行がすでに適用されているかどうかを確認できます。次に、「SELECT FOR UPDATE」を使用して、次のような移行を実装します(疑似コード)。

  • トランザクションを開始
  • _SELECT FROM Migrations FOR UPDATE WHERE MigrationLabel='MyMigration42'_
  • 前のステートメントが値を返す場合、トランザクションを終了します
  • 移行を適用する(失敗した場合はロールバックし、失敗をログに記録してトランザクションを終了する)
  • INSERT 'MyMigration42' INTO Migrations(MigrationLabel)
  • トランザクションを終了する

これは、ロックメカニズムを直接「組み込み済みの移行でした」テストに組み込みます。

この設計では-理論的には-どのアプリケーションが実際に適用するのかを移行ステップに認識させないようにすることができます-ステップ1はapp1、ステップ2はapp2、ステップ3はアプリ3、ステップ4はapp1によって適用される可能性があります再び、など。ただし、他のアプリインスタンスが使用されている限り、移行を適用しないこともお勧めします。質問で述べたように、並列配置はすでにこの制約を考慮している場合があります。

4
Doc Brown

多分あなたは、複数のノードでデータベースの移行をサポートするライブラリを見つけることができます。

Java世界の2つのライブラリについて知っています。どちらも必要なものをサポートしています。

  • Liquibase :それらから [〜#〜] faq [〜#〜]Liquibaseは分散ロックシステムを使用して1つのプロセスのみを許可します一度にデータベースを更新します。他のプロセスは、ロックが解放されるまで単に待機します。
  • Flyway :それらから ダウンロードページ複数のノードを並行して安全✓

おそらく他にもJavaおよび他の言語用のツールがあります。


このようなツールを使用できない(または使用したくない)場合は、テーブルをロックとして、または移行ログとして使用できます。例については、 Doc Browns answer を参照してください。

1
siegi