22

自明ではない本番環境でdjangoアプリケーションを使用している人から、データベースの移行をどのように処理しますか?あることは知っていますがsouth、何か実質的なものが含まれていると、それはかなり見逃してしまうようです。

他の2つのオプション(私が考えるか使用したことがある)は、テストデータベースで変更を行い、次に(アプリでオフラインにして)、そのSQLエクスポートをインポートすることです。または、おそらくよりリスクの高いオプションであり、本番データベースで必要な変更をリアルタイムで実行し、問題が発生した場合はバックアップに戻ります。

通常、データベースの移行とスキーマの変更をどのように処理しますか?

4

6 に答える 6

23

この問題には 2 つの部分があると思います。

まず、データベース スキーマとその変更を管理します。これは South を使用して行い、作業モデルと移行ファイルの両方を SCM リポジトリに保持します。安全 (またはパラノイア) のために、移行を実行する前に (そして本当に怖い場合は後で) データベースのダンプを取ります。これまでのところ、南はすべての要件に適しています。

2 つ目は、South によって生成された移行ファイルを実行するだけでなく、スキーマの変更をデプロイすることです。私の経験では、通常、データベースを変更するには、デプロイされたコードを変更する必要があります。小規模な Web ファームであっても、展開されたコードをデータベース スキーマの現在のバージョンと同期させることは簡単ではないかもしれません。さまざまなキャッシュ レイヤーと、既にアクティブなサイト ユーザーへの影響を考慮すると、これはさらに悪化します。この問題の扱いはサイトによって異なります。万能の答えはないと思います。


この問題の 2 番目の部分を解決することは、必ずしも簡単ではありません。万能のアプローチがあるとは思いません。また、状況に最も適したソリューションを提案するための Web サイトと環境に関する十分な情報がありません。ただし、ほとんどの状況で展開をガイドするのに役立ついくつかの考慮事項があると思います。

場合によっては、サイト全体 (Web サーバーとデータベース) をオフラインにすることもできます。これは、更新を管理するための最も簡単な方法です。しかし、頻繁なダウンタイム (計画されている場合でも) は、ビジネスを迅速に進めるための良い方法であり、小さなコード変更でもデプロイするのが面倒になり、大規模なデータセットや複雑な移行がある場合は何時間もかかる可能性があります。とはいえ、私が管理を手伝っているサイト (すべて内部サイトであり、通常は営業日の勤務時間中にのみ使用されるサイト) では、このアプローチは驚くほどうまく機能します。

master データベースのコピーに変更を加える場合は注意してください。ここでの主な問題は、サイトがまだ稼働しており、おそらくデータベースへの書き込みを受け入れていることです。後で使用するためにクローンを移行している間、マスター データベースに書き込まれたデータはどうなりますか? サイトは常にダウンしているか、一時的に読み取り専用の状態にする必要があります。そうしないと、サイトが失われます。

変更に後方互換性があり、Web ファームを使用している場合は、稼働中の運用データベース サーバーを更新し (ほとんどの場合、これは避けられないと思います)、ファーム内のノードをノードから取り出すことで段階的に更新することで回避できる場合があります。短期間のロードバランサー。これは問題なく動作しますが、ここでの主な問題は、既に更新されているノードが古いノードでサポートされていない URL のリクエストを送信した場合、ロード バランサー レベルでそれを管理できないため、失敗することです。

他のいくつかの方法がうまく機能するのを見たり聞いたりしました。

1 つ目は、すべてのコード変更を機能ロックにラップすることです。これは、サイト全体の構成オプションを使用して実行時に構成できます。これは本質的に、すべての変更をオフにしたコードをリリースできることを意味し、サーバーに必要なすべての更新を行った後、構成オプションを変更して機能を有効にします。しかし、これは非常に重いコードになります...

2 つ目は、コードに移行を管理させることです。コードへの変更が、実行時に移行を処理するような方法で記述されているサイトについて聞いたことがあります。使用されているスキーマのバージョンと、返されたデータの形式を検出できます。データが古いスキーマからのものである場合は、その場で移行を行い、データが既に新しいスキーマからのものである場合は何もしません。 . 自然なサイトの使用状況から、データの大部分はサイトを使用する人々によって移行され、残りはいつでも移行スクリプトで実行できます.

しかし、この時点で Google はあなたの友達になると思います。なぜなら、私が言うように、解決策は非常にコンテキスト固有であり、この答えが無意味になり始めるのではないかと心配しているからです...「ゼロ ダウン タイム デプロイ」などを検索すると、アイデア次第でこんな結果に・・・

于 2012-06-02T06:45:14.993 に答える
4

コードベースが 40,000 行以下の運用サーバーに South を使用していますが、これまでのところ問題はありません。また、一部のモデルについていくつかの主要なリファクタリングを実施しましたが、問題はありませんでした。

また、モデルのバージョン管理も行っています。これにより、ソフトウェア側でモデルに加えた変更を元に戻すことができます。南は実際のデータに重点​​を置いています。Django Reversionを使用します

于 2012-05-31T05:35:48.500 に答える
3

私は時々、この問題に対して型破りなアプローチをとってきました (他の回答を読んでも、おそらくそれほど型破りではないでしょう)。私はdjangoで試したことがないので、いくつか実験しました。

つまり、古いスキーマに起因する例外をコードでキャッチし、適切なスキーマのアップグレードを適用します。私はこれが受け入れられた答えになるとは思っていません-それはいくつかの場合にのみ適切です(そして、まったくないと主張する人もいます)。しかし、醜いアヒルのような優雅さがあると思います。

もちろん、いつでも本番環境にリセットできるテスト環境があります。そのテスト環境を使用して、スキーマを更新し、それに対してコードを記述します - いつものように。

次に、スキーマの変更を元に戻し、新しいコードを再度テストします。結果のエラーをキャッチし、スキーマのアップグレードを実行してから、エラーのあるクエリを再試行します。

アップグレード関数は、「害を及ぼさない」ように作成する必要があります。これにより、複数回呼び出された場合 (実稼働環境に置かれたときに発生する可能性があります)、1 回だけ動作します。

実際の python コード - これを settings.py の最後に置いて概念をテストしましたが、おそらく別のモジュールに保持する必要があります。

from django.db.models.sql.compiler import SQLCompiler
from MySQLdb import OperationalError

orig_exec = SQLCompiler.execute_sql
def new_exec(self, *args, **kw):
    try:
        return orig_exec(self, *args, **kw)
    except OperationalError, e:
        if e[0] != 1054: # unknown column
            raise
        upgradeSchema(self.connection)
        return orig_exec(self, *args, **kw)
SQLCompiler.execute_sql = new_exec

def upgradeSchema(conn):
    cursor = conn.cursor()
    try:
        cursor.execute("alter table users add phone varchar(255)")
    except OperationalError, e:
        if e[0] != 1060: # duplicate column name
            raise

運用環境が最新の状態になったら、この自己アップグレード コードをコードベースから自由に削除できます。しかし、そうしなくても、コードは重要で不要な作業を行っていません。

例外クラス (私の場合は MySQLdb.OperationalError) と番号 (私の場合は 1054 "unknown column" / 1060 "duplicate column") をデータベース エンジンとスキーマの変更に合わせて調整する必要がありますが、それは簡単なはずです。

実行中のSQLが他の問題ではなく問題のスキーマ変更のために実際にエラーであることを確認するために追加のチェックを追加することをお勧めしますが、そうしない場合でも、無関係な例外が再発生するはずです。唯一のペナルティは、例外が発生する前に、アップグレードと不適切なクエリを 2 回試行することになります。

Python について私が気に入っている点の 1 つは、このように実行時にシステム メソッドを簡単にオーバーライドできることです。それは非常に柔軟性を提供します。

于 2012-06-08T16:09:28.263 に答える
2

データベースが自明ではなく、Postgresql である場合、次のような SQL に関する優れたオプションがたくさんあります。

  • スナップショットとロールバック
  • バックアップ サーバーへのライブ レプリケーション
  • 試用版のアップグレード後に公開

トライアル アップグレード オプションは便利です (ただし、スナップショットと連携して行うのが最適です)。

su postgres
pg_dump <db> > $(date "+%Y%m%d_%H%M").sql
psql template1
# create database upgrade_test template current_db
# \c upgradetest
# \i upgrade_file.sql
...assuming all well...
# \q
pg_dump <db> > $(date "+%Y%m%d_%H%M").sql # we're paranoid
psql <db>
# \i upgrade_file.sql

上記の配置が気に入っているが、アップグレードを 2 回実行するのにかかる時間が心配な場合は、書き込み用にdbをロックし、 upgradetestへのアップグレードがうまくいった場合は、 dbdboldに、upgradetestdbに名前を変更できます。多くのオプションがあります。

行いたいすべての変更をリストした SQL ファイルがある場合、非常に便利な psql コマンド\set ON_ERROR_STOP 1. これにより、問題が発生した瞬間にアップグレード スクリプトが停止します。そして、多くのテストを行うことで、何も起こらないことを確認できます。

このStackOverflowの回答に記載されている数とともに、利用可能なデータベーススキーマ差分ツールが多数あります。しかし、基本的には手作業で行うのはかなり簡単です...

pg_dump --schema-only production_db > production_schema.sql
pg_dump --schema-only upgraded_db > upgrade_schema.sql
vimdiff production_schema.sql upgrade_schema.sql
or
diff -Naur production_schema.sql upgrade_schema.sql > changes.patch
vim changes.patch (to check/edit)
于 2012-06-07T22:41:25.533 に答える
1

南はどこでも使用されていません。私の組織と同様に、3 つのレベルのコード テストがあります。1 つはローカル開発環境、1 つはステージング開発環境、3 つ目は本番環境です。

Local Dev は開発者の手元にあり、必要に応じてプレイできます。次に、本番環境と同一に保たれるステージング dev が続きます。もちろん、ライブ サイトで db の変更を行う必要があります。まずステージングで db の変更を行い、すべてが正常に機能しているかどうかを確認してから、本番環境を手動で変更します。 db を再度ステージングと同じにします。

于 2012-06-03T17:37:57.920 に答える