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


10

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

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

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

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

<tl-dr>

次のようなケースを考えましたが、そういうシナリオはあまり経験がないので、他の人の意見を聞いてみました。

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

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

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

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

</ tl-dr>


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

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


回答:


8

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

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

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

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

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

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

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

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

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

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


変更のためにログを維持する必要がある1000個のテーブルがある場合、1000個のシャドウテーブルを作成する必要があります。変更をキャプチャするための1000トリガー?はいの場合、それは偽のアイデアです...変更されたデータをキャプチャしてログに記録するために、単一の履歴テーブルと単一のトリガーを作成できます。そのテーブルに古い行と新しい行のデータをxmlとして保存できます。
Thomas

1
1000個のテーブルの場合、システムデータディクショナリから定義を読み取り、トリガーとテーブル定義を生成するユーティリティを記述します。私は560テーブルのシステムでそれを実行しましたが、うまく機能します。
ConcernedOfTunbridgeWells 2014年

0

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

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


0

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.