2

バージョニングを使用して動的プロパティのスキーマを設計する際に問題が発生します。次のユースケースを想定します。

Actorとを含むテーブルがidありますname(簡単にするため)。私の場合の上限は、このテーブルに約100万のエントリが含まれていることです。

さらに、すべてのアクターにプロパティが割り当てられます。当時は物件がわからないので、物件を管理するためのテーブルが必要です。私はテーブルについて考えましたProperty。結果のn:m関係は、主キーとプロパティ値(タイプ?)の間のテーブルで解決されActorますProperty

現時点では、これは非常に扱いやすいようです。それぞれが10個のプロパティを持つ100万個のエントリがある場合、ActorPropertyテーブルには1,000万個のノードが含まれます。btreeインデックス(log2(n))では、これは問題ないと思います。

今、私が苦労している部分が来ます。プロパティはどういうわけか追跡する必要があります。時間の経過とともにこれらのプロパティは変化しますが、履歴が失われることはありません。ほとんどの場合、タイムスタンプを使用して実行されます。複数のプロパティが同時に更新されることに注意してください。例:私は毎日すべてのアクターのスナップショットを撮り、何かが変更された場合は、変更されたすべてのプロパティを同時に更新します。これにより、1年あたり365のタイムスタンプが得られます。

別のテーブルを使用してバージョン(タイムスタンプ)を管理し、テーブルに別の外部キーを追加すると、ActorProperty365*1,000万のエントリが得られます。これは私が今までに得た最大のものでなければなりません。ほとんどの場合、データセットは大幅に小さくなります。

私の質問は今、パフォーマンスにもっと取り組むことです。インデックスに関する次の回答を読みました。データベースのインデックス作成はどのように機能しますか。その量のエントリを含むテーブルをクエリするのは、ひどく遅いのではないでしょうか。クエリの例は次のようになります。指定されたタイムスタンプid=xですべてのプロパティを持つ最初の100人のアクター。また、私が考えているスキーマはおそらく最高ではないように感じます。よりスケーラビリティのあるスキーマについての提案やアイデアはありますか?

ちなみに、現在NoSqlのアプローチも評価しているので、とりあえずリレーショナルアプローチに集中したいと思います。私の目的は、さまざまなテクノロジーの長所と短所を収集し、説明されているユースケースの理論的なスキーマまたはモデルを用意することです。そして、リレーショナルデータベースでの最適なモデルでのパフォーマンスは、私が評価したり見つけたりするのに苦労しているようです。

ありがとう!

4

4 に答える 4

1

プロパティはどういうわけか追跡する必要があります

ここでは、正確にどのように追跡するかが重要です。最も単純なケースでは、いつでも状態を照会する必要があります。そのため、解決策は、分解テーブルに複数の時間依存レコードを含めることです。

create table actor_property (
  actor_id INT NOT NULL,
  property_id INT NOT NULL,
  starttime DATE NOT NULL,
  endtime DATE NOT NULL DEFAULT 99991231
  PRIMARY KEY (actor_id, property_id, starttime, endtime) 
);

この結果、アクターをプロパティにリンクしようとしたときに、リンクがテーブルにすでに存在する場合に対処する必要があります(トリガーでテーブルを更新することはできませんが、競合をチェックして強制することはできます)例外)。その後、いつでもデータの状態を照会できます。

SELECT a.name, property.name
FROM actor a
INNER JOIN actor_property ap
   ON a.id=ap.actor_id
INNER JOIN property p
   ON p.property_id
WHERE $snapshot_date >= ap.starttime
AND $snapshot_date <= ap.endtime

上記の場所にあるactor_propertyの現在のレコードのマテリアライズド・ビューを使用すると、関係が変更される頻度に応じて、わずかに高速になります。

その量のエントリを含むテーブルをクエリするのは、ひどく遅いのではないでしょうか。

実際には、データセット全体を頻繁に分析する必要がない限り、ほとんどの操作は行の小さなサブセットのみを調べ、通常、データベースはホットデータの領域を進化させます-読み取りキャッシングはmysqlのクエリキャッシング(非常に具体的です)よりもはるかに効果的です。

于 2012-06-21T12:57:18.007 に答える
1

アプリケーションの 1 つで、やや似たデザインを使用しました。

まず、プロパティのセットは (理論的には) それほど大きくないと思いますので、共有することをお勧めします。この目的のためにPROPERTY_TYPE、一意IDNAME列を持つテーブルを作成します。このように、メインPROPERTYテーブルACTOR_IDPROPERTY_TYPE_IDVALUE列があり、2 つの利点があります。

  1. すべてのユースケースで一度だけプロパティ名を保存するため、テーブルのサイズが大幅に縮小されます。
  2. クエリのパフォーマンスが大幅に向上します。

次に、プロパティの追跡に進みます。オブジェクトのインスタンスを時間内に追跡し、各インスタンスに開始時刻と終了時刻があるアプローチが気に入っています。now() BETWEEN start_dt AND coalesce(end_dt, now())プロパティの現在アクティブなインスタンスは、開いているインスタンスend_dtが事実上であるため、を使用して見つけることができますNULL

スキーマは次のようになります。

CREATE TABLE actor (
    actor_id   integer not null,
    actor_name varchar(100) not null,
    PRIMARY KEY (actor_id)
    );
CREATE TABLE property_type (
    property_type_id   integer not null,
    property_type_name varchar(100) not null,
    PRIMARY KEY (property_type_id),
    UNIQUE (property_type_name)
    );
CREATE TABLE actor_property (
    actor_id         integer not null,
    property_type_id integer not null,
    property_value   varchar(500) not null,
    start_dt         timestamp not null,
    end_dt           timestamp
    PRIMARY KEY (actor_id, property_type_id, start_dt)
    );

実装に関する注意事項:

  1. プロパティの更新は、事実上、アトミックなインスタンスのクローズ + インスタンスの作成操作です。したがって、これをブロックにラップするかSTART TRANSACTION; ... COMMIT;、(私はその方が好きです) ジョブを実行する関数を作成することをお勧めします。
  2. いずれにせよ、DB 側の関数を使用するのは良いスタイルです。
  3. すべてのテーブルの主キーの背後に暗黙的なインデックスがあり、期待されるパフォーマンスが得られます。
  4. テーブル内の潜在的な 365e6 行はactor_property、最新のハードウェアでは大した問題ではありません。インデックスが適切に配置され、バランスが取れている場合、最悪のシナリオでは、このテーブルから 1 つのエントリをクエリするために最大 30 回のディスク ページ読み取りを実行します。
于 2012-06-21T13:25:33.397 に答える
1

@symcbean と @vyegorov のアプローチはどちらも正しいです。最新のハードウェアでは、単純なクエリは、話しているデータの量に問題はありません。

ただし、スキーマ設計 (一般に「エンティティ/属性/値」または EAV として知られています) には、クエリに関して考慮すべきいくつかの欠点があります。

一般的なリレーショナル ステートメントは非常に複雑になる可能性があり、多くの場合処理が遅くなります。たとえば、プロパティ "height" > 1.9、プロパティ "age" <= 25、プロパティ "agent" が 'sleazeball' とは異なり、プロパティ "hard to work" が現在発生していないアクターを検索するクエリを想像してください。と"。

「property_value」列が varchar の場合、数値比較は直感に反する傾向があります。

"in"、"not in" などの検索は厄介です。

「agent が sleazeball のようではない」と解釈することは、次の 2 つのことを意味する可能性があります。agent と呼ばれるプロパティがあり、その値が sleazeball ではないか、agent と呼ばれるプロパティすらありません。

これらすべての問題に言及する理由は、設計をもう少し進められるようにするためです。パフォーマンスを仮説的なものとして考えるだけでは十分ではなく、現実的なシナリオを考える必要があります。

于 2012-06-21T13:53:21.180 に答える
0

状況によっては、問題を「現在のプロパティ」と「過去のプロパティ」に分割すると、パフォーマンスが向上する場合があります。テーブルサイズの増加による指数関数的なコストを大幅に削減するため、さまざまな ORM がバージョン管理可能な動作のためにこのアプローチを採用しています。

したがって、あなたの場合、Actorテーブルが次のものとペアになっていることを考慮してください。

  • ActorProperty(fk = actor_id)
  • ActorPropertyVersionable(fk = actor_id, version_num)

したがって、アクターの新しいプロパティを作成するときは、まず既存の値をコピーしてバージョン管理可能なテーブルに挿入し、次に新しい値を現在のテーブルに追加する必要があります。これをトランザクションでラップして、安全に保ちます。

一般に、プロパティ クエリはほとんどの場合、現在のプロパティ値に関心があり、過去の値へのアクセスが必要になる頻度ははるかに低くなります (もちろん、独自のユース ケースについて判断する必要があります)。データの問い合わせごとに 2 つの異なるクエリ (現在の値、過去の値) が必要ですが、パフォーマンス上の利点はそれだけの価値がある場合があります。

于 2012-06-21T17:06:10.223 に答える