web-dev-qa-db-ja.com

通常、データベースの行のすべての変更の記録はどのように保存されますか?

私が取り組んでいるプロジェクトでは、データベースの一部のテーブルの行に対するすべての変更を追跡する必要がありますさらに監査またはロールバックします。行を誰がどのIPアドレスからいつ変更したかを簡単に見つけ、以前のバージョンを復元できるようにする必要があります。

同様のものが、たとえばStack Exchangeでも使用されています。他の人の質問を変更すると、自分が変更したことがわかり、変更をロールバックできます。

データベース内のオブジェクトへのすべての変更を保存するために使用される一般的なテクニックは何ですか、現在のスキーマが平均的なビジネスアプリとほとんど同じプロパティ(以下)を持っているとすると、

  • オブジェクトのサイズは比較的小さいです。たとえば、いくつかのnvarchar(1000)があるかもしれませんが、バイナリデータの巨大なblobはありません。これは、ディスクに直接格納され、直接アクセスされますnot = Microsoft SQLを通じてfilestream
  • データベースの負荷はかなり低く、データベース全体はサーバー上の1つの仮想マシンによって処理されます。
  • 以前のバージョンへのアクセスは、最新バージョンへのアクセスほど高速である必要はありませんが、それでも最新の状態でなければならず、遅くない²。

<tl-dr>

次のようなことを考えましたが、そういうシナリオはあまり経験がないので、他の人の意見を聞きます。

  1. IDとバージョンで行を区別して、すべてを同じテーブルに格納します。 IMO、それは深刻な愚かであり、パフォーマンスレベルで遅かれ早かれ傷つくでしょう。このアプローチでは、最新のアイテムとバージョントレースに異なるセキュリティレベルを設定することも不可能です。最後に、すべてのクエリを記述するのはより複雑になります。実際、最新のデータにアクセスするには、すべてをIDでグループ化し、各グループで最後のバージョンを取得する必要があります。

  2. 1つのテーブルに最新バージョンを保存し、変更のたびに、古いバージョンを別のスキーマの別のテーブルにコピーします。問題は、たとえ変更されていなくても、常にすべての値を保存することです。変更されていない値をnullに設定することは、値がnullまたはnullに変更されたときにも追跡する必要があるため、解決策ではありません。

  3. 最新バージョンを1つのテーブルに保存し、変更されたプロパティのリストと以前の値を別のテーブルに保存します。これには2つの欠点があるようです。最も重要なのは、同じ列で以前の値の異種タイプをソートする唯一の方法は、binary(max)を使用することです。 2つ目は、以前のバージョンをユーザーに表示するときに、このような構造を使用するのがより困難になると私は考えています。

  4. 前の2つのポイントと同じことを行いますが、バージョンを別のデータベースに保存します。パフォーマンス面では、同じデータベースに以前のバージョンを置くことで最新バージョンへのアクセスが遅くなるのを避けるために興味深いかもしれません。それでも、これは時期尚早の最適化であり、同じデータベースに古いバージョンと最新のバージョンがあることがボトルネックであるという証拠がある場合にのみ実行する必要があると思います。

</ tl-dr>


¹たとえば、HTTPログの場合と同様にログファイルに変更を保存し、サーバーの負荷が最も低い夜間にログからデータベースにデータをフラッシュすることはできません。異なるバージョンに関する情報は、すぐに、またはほぼすぐに入手できる必要があります。数秒の遅延は許容範囲です。

²情報へのアクセス頻度はそれほど高くなく、特定のユーザーグループのみがアクセスできますが、バージョンのリストが表示されるまで30秒間待つように強制することはできません。この場合も、数秒の遅延は許容されます。

10

この種の監査ロギングを行う通常の方法は、シャドウテーブルを作成し、監査しているベーステーブルのトリガーを使用して変更をログに記録することです。他のテーブルは、パフォーマンスのためにそれが必要な場合は別の物理ディスクに配置でき、データの迅速な取得をサポートする必要がある場合はインデックスを付けることができます。

テーブルの構造は元のテーブルとほぼ同じですが、変更が行われた日時列と、行が挿入、変更、削除されたかどうかを示すマーカーが含まれます。バージョンのシーケンスは、タイムスタンプによって実行できます。

日付の変更は、デフォルトのgetdate()で、datetime列をnull以外にすることで実行できます。監査ユーザー列は、デフォルトでSuser_Sname()に設定されたnull以外の列を持つユーザーをキャプチャします。実際のユーザーがセッションで偽装されていると想定すると、変更を行うユーザーのIDが取得されます。

データベースには、Webサーバーに接続しているIPアドレスを認識する方法がありません。アプリケーションは、トランザクションでIPアドレスを明示的にキャプチャしてログに記録する必要があります。

監査するテーブルが多数ある場合は、システムデータディクショナリのメタデータを使用して、プログラムでトリガーを生成できます。

このソリューションは、いくつかの理由で断然最善です。

  • アプリケーションによって行われた変更だけでなく、テーブルへの変更をキャプチャします。

  • 監査テーブルを別のディスクセットに配置して、プライマリテーブルのI/O負荷を軽減できます。

  • テーブルと監査ログテーブルの和集合に基づくビューを使用して、現在のバージョンを含む履歴全体を表示できます。

  • 必要に応じて監査ログテーブルにインデックスを付けて、監査ユーザーが応答してクエリを実行できるようにすることができます。いつものように、インデックスの選択は、クエリのパフォーマンスと更新のオーバーヘッドの間のトレードオフです。

CMSのバージョン管理について。 drupalの場合、古い値を格納するエンティティのすべてのフィールドに対して特別なテーブルが作成されます。このようなコンセプトにより、データを細かく操作できますが、コストがかかると思いますが、私の独自の解決策はオブジェクトをxml形式に変換し、他のフィールド(changetime、id ...)とともに文字列として保存します

0
Bourkadi

単一のテーブルを使用してデータのすべてのバージョンを格納する多くのCMSシステム(Wordpressを含む)を知っています。しかし、繰り返しになりますが、これはブログの投稿があるテーブルに対してのみ行う必要があります。 the Wordpressデータベース構造 を参照してください。

また、レコードの数と各行が通過するリビジョンの数は、決定に重要な役割を果たします。