データベース設計で不変性を優先する


26

Joshua BlochのEffective Javaの項目の1つは、クラスがインスタンスの変更をできる限り少なくし、できればまったく許可しないという概念です。

多くの場合、オブジェクトのデータは何らかの形式のデータベースに保存されます。これにより、特に大規模システム内の単一のエンティティを表すテーブルについて、データベース内の不変性の考えを考えるようになりました。

私が最近実験しているのは、これらのオブジェクトを表すテーブル行に対して行う更新を最小限に抑え、可能な限り挿入を実行しようとするアイデアです。

最近試したものの具体例。後で追加データを含むレコードを追加する可能性があることがわかっている場合は、それを表す別のテーブルを作成します。次の2つのテーブル定義のようなものです。

create table myObj (id integer, ...other_data... not null);
create table myObjSuppliment (id integer, myObjId integer, ...more_data... not null);

これらの名前が逐語的ではなく、単にアイデアを示すためであることは、願わくば明白です。

これは、データ永続性モデリングへの合理的なアプローチですか?特に、レコードが最初に作成されたときに存在しなかった可能性のあるデータのNULLを埋めるために、テーブルで実行される更新を制限することを試みる価値はありますか?このようなアプローチが後に激しい痛みを引き起こす可能性があるときはありますか?


7
これは問題のない解決策だと思います...更新を避けるために精巧な適応を作成するのではなく、更新する必要があります。
フォスコ

解決策の直感的なアイデアを念頭に置いて、できるだけ多くの人がそれを実行したい、そしてこれが私が抱えている問題に対する最善の解決策ではないかもしれないことを認識している過程でそれが問題だったと思います。他の場所で見つからない場合は、問題に関する別の質問を開くことがあります。
エド・カレル

1
データベースの更新を回避する正当な理由があります。ただし、これらの理由が明らかになった場合、それはより最適化の問題であるため、問題があることを証明せずに行うべきではありません。
ダイエット仏

6
データベース内の不変性には強い議論があると思います。それは多くの問題を解決します。ネガティブなコメントは、オープンマインドな人々から来たものではないと思います。インプレース更新は、非常に多くの問題の原因です。私たちはそれをすべて後方に持っていると主張します。インプレース更新は、もはや存在しない問題に対するレガシーソリューションです。ストレージは安いです。どうしてですか?監査ログ、バージョン管理システムを備えたDBシステムの数は、誰もが知っているように、スケールの遅延をサポートする能力を必要とする分散レプリケーションを必要とします。不変性はこれをすべて解決します。
シーラス

@Fosco一部のシステムでは、データを削除しないことが絶対に必要です(使用を含むUPDATE)。医師の医療記録のように。
イズカタ

回答:


25

不変性の主な目的は、メモリ内のデータが無効な状態にある瞬間がないことを保証することです。(もう1つは、数学表記がほとんど静的であるため、不変なものは数学的に概念化およびモデル化するのが簡単だからです。)メモリ内で、別のスレッドが作業中にデータを読み書きしようとすると、破損する可能性があります。それ自体が破損した状態にある可能性があります。マルチスレッドアプリケーションで、オブジェクトのフィールドに複数の割り当て操作がある場合、別のスレッドがその間の操作を試みる可能性があります。

不変性は、最初にすべての変更をメモリ内の新しい場所に書き込み、次に、すべてのCPUでアトミックである新しいオブジェクトを指すようにオブジェクトへのポインターを書き換える1回の急降下ステップとして最終割り当てを行うことにより、これを改善します操作。

データベースはアトミックトランザクションを使用して同じことを行います。トランザクションを開始すると、すべての新しい更新がディスク上の新しい場所に書き込まれます。トランザクションが完了すると、ディスク上のポインターが新しい更新のある場所に変更されます。これは、他のプロセスがそれに触れることができない短時間で行われます。

これは、新しいテーブルを作成するという考えとまったく同じですが、より自動で柔軟性が高い点が異なります。

したがって、あなたの質問に答えるために、はい、データベースでは不変性は良いですが、いいえ、その目的のためだけに別のテーブルを作成する必要はありません。データベースシステムで利用可能なアトミックトランザクションコマンドを使用できます。


答えてくれてありがとう。この見方は、直観が混乱して2つの異なるアイデアを1つのパターンに結合しようとしていたことを理解するために必要なものでした。
エドキャレル

8
それには雰囲気よりも少し多くがあります。私がOOPコンテキストで不変性を支持することで最もよく見られる議論は、不変オブジェクトは、コンストラクターで状態を一度検証するだけでよいというものです。それらが可変である場合、状態を変更できるすべてのメソッドは、結果の状態がまだ有効であることを確認する必要があり、これによりクラスが非常に複雑になります。この引数はデータベースにも適用される可能性がありますが、db検証ルールは手続き型ではなく宣言型になる傾向があるため、クエリごとに複製する必要がないため、はるかに脆弱です。
デイブシェロマン

24

それは、不変性から得られるメリットに依存します。宮坂Reの答えは1つ(無効な中間状態の回避)に対応していましたが、もう1つあります。

突然変異は破壊的更新と呼ばれることもあります。オブジェクトを突然変異させると、古い状態は失われます(何らかの方法で明示的に保存するための追加の手順を実行しない限り)。対照的に、不変データでは、何らかの操作の前後で状態を同時に表現したり、複数の後続状態を表現したりするのは簡単です。単一の状態オブジェクトを変更することによって幅優先検索を実装しようとすることを想像してください。

これはおそらく、データベースの世界で一時データとして最も頻繁に表示されます。先月、基本プランを使用していたが、16日にプレミアムプランに切り替えたとしましょう。あなたがどのプランを使用しているかを示すフィールドを単に上書きした場合、請求を正しく行うことが困難になる可能性があります。また、傾向を分析する機能を逃す可能性があります。(ねえ、このローカル広告キャンペーンが何をしたか見てください!)

とにかく「データベース設計における不変性」と言うとき、それが私の頭に浮かぶものです。


2
3番目の段落には同意しません。履歴(監査ログ、計画変更のログなど)が必要な場合は、このために別のテーブルを作成する必要があります。Customerユーザーが計画を変更したことを覚えておくためにテーブルの50フィールドすべてを複製しても、パフォーマンス上の大きな欠点、時間の経過に伴う選択の遅延、ログと比較したより複雑なデータマイニング、および無駄なスペース以外は何ももたらされません。
アルセニムルゼンコ

6
@MainMa:代わりに「一時データベースについて読む」と言ったほうがいいでしょう。私の例は、一時的なデータが何であるかのスケッチとして意図されました。それが常に変化するデータを表現する最良の方法だとは言いません。一方、一時データのサポートは現在かなり貧弱ですが、一時的なデータを変更ログなどの「第2クラス」表現に委ねるのではなく、データベース自体に収容する傾向があると思います。
ライアンカルペッパー

監査テーブルの変更履歴を保持する場合(この機能の例として、スプリングブートとハイバネート)
モハマドナジャール

14

データベース、または少なくとも不変性の錯覚を提供するデータベースの不変性から得られる利点に興味がある場合は、Datomicをチェックしてください。

Datomicは、Rich HickeyがThink Relevanceと提携して発明したデータベースです。アーキテクチャ、目標、データモデルについて説明したビデオがたくさんあります。検索infoq、特に1つは、Datomic、Database as a Valueというタイトルです。confreaksでは、2012年のeuroclojureカンファレンスでリッチヒッキーの基調講演をご覧いただけます。confreaks.com/videos/2077-euroclojure2012-day-2-keynote-the-datomic-architecture-and-data-model

より開発志向のvimeo.com/53162418で講演があります。

stuart halloway at.pscdn.net/008/00102/videoplatform/kv/121105techconf_close.htmlからの別のものです

  • Datomicは、5タプル[E、A、V、T、O]のデータムと呼ばれる時間の事実のデータベースです
    • EエンティティID
    • エンティティの属性名(名前空間を持つことができます)
    • V属性の値
    • TトランザクションID。これには時間の概念があります。
    • Oアサーション(現在または現在の値)、拒否(過去の値)の1つの操作。
  • EDN(Extensible Data Notation)と呼ばれる独自のデータ形式を使用します
  • トランザクションはACIDです
  • クエリ言語としてデータログを使用しますが、SQL +再帰クエリとして宣言的です。クエリはデータ構造で表され、jvm言語で拡張されるため、clojureを使用する必要はありません。
  • データベースは、3つの個別のサービス(プロセス、マシン)で分離されます。
    • トランザクション
    • ストレージ
    • クエリエンジン。
  • 各サービスを個別にスケーリングできます。
  • オープンソースではありませんが、無料の(ビールのように)Datomicのバージョンがあります。
  • 柔軟なスキーマを記述できます。
    • 属性のセットが開いています
    • いつでも新しい属性を追加します
    • 定義やクエリに厳格さはありません

さて、情報は時間通りに事実として保存されるので:

  • データベースにファクトを追加するだけで、それらを削除することはありません(法律で義務付けられている場合を除く)
  • すべてを永久にキャッシュできます。クエリエンジンは、メモリ内データベースとしてアプリケーションサーバーに存在します(JVM言語の場合、非JVM言語はREST APIを介してアクセスできます)。
  • 過去の時点でクエリを実行できます。

データベースは値であり、クエリエンジンへのパラメーターであり、QEは接続とキャッシュを管理します。dbは値として、またメモリ内の不変のデータ構造として見ることができるため、実際のデータベースを変更することなく、「将来」の値から作成された別のデータ構造とマージし、それをQEとクエリに渡すことができます。

codeqと呼ばれるRich Hickeyのオープンソースプロジェクトがあり、github Datomic / codeqで見つけることができます。これはgitモデルを拡張し、datomicフリーデータベースにgitオブジェクトへの参照を保存し、コードのクエリを作成します。datomicの使用方法のを見ることができます。

datomicはACID NoSQLであると考えることができ、データを使用すると、テーブルまたはドキュメント、またはKvストアまたはグラフをモデル化できます。


7

更新を避け、挿入を好むという考え方は、データソースをイベントソースとして構築する背景にある考え方の1つであり、CQRSと一緒に使用されることがよくあります。イベントソースモデルでは、更新はありません。集約は、その「変換」(イベント)のシーケンスとして表され、その結果、ストレージは追加専用になります。
このサイトには、CQRSとイベントソーシングに関する興味深い議論があります(興味がある場合)。


CQRSとイベントソーシングが最近注目を集めています。
グルシャン

6

これは、データウェアハウジングの世界で「緩やかに変化するディメンション」と呼ばれるもの、および他のドメインの「テンポラル」または「バイテンポラル」テーブルと非常に密接な関係があります。

基本的な構成は次のとおりです。

  1. 生成された代理キーを常に主キーとして使用します。
  2. あなたが記述しているものの一意の識別子が「論理キー」になります。
  3. 各行には、少なくとも「ValidFrom」タイムスタンプと、オプションで「ValidTo」タイムスタンプ、さらにオプションで「最新バージョン」フラグが必要です。
  4. 論理エンティティの「作成」で、現在のタイムスタンプの「有効な開始」を持つ新しい行を挿入します。オプションのValidToを「forever」(9999-12-31 23:59:59)に設定し、最終バージョンを「True」に設定します。
  5. 論理エンティティの後続の更新時。少なくとも上記のように新しい行を挿入します。以前のバージョンのValidToを「now()-1秒」に、最新バージョンを「False」に調整する必要がある場合もあります
    1. 論理的な削除(ValidToタイムスタンプでのみ機能します!)では、現在の行のValidToフラグを「now()-1秒」に設定します。

このスキームの利点は、任意の時点で論理エンティティの「状態」を再作成できることです。時間の経過に伴うエンティティの履歴があり、「論理エンティティ」が頻繁に使用される場合の競合を最小限に抑えます。

欠点は、より多くのデータを格納し、より多くのインデックスを維持する必要があることです(少なくとも論理キー+ ValidFrom + ValidToで)。論理キー+最新バージョンのインデックスは、ほとんどのクエリを大幅に高速化します。また、SQLが複雑になります!

履歴を維持する必要があり、特定の時点でエンティティの状態を再作成する必要がある場合を除き、これを行う価値があるかどうかはユーザー次第です。


1

不変のデータベースを持つもう1つの考えられる理由は、より良い並列処理をサポートすることです。更新が順不同で行われると、データが永久に混乱する可能性があるため、ロックを発生させて並列パフォーマンスを破壊する必要があります。多くのイベントの挿入は任意の順序で実行でき、すべてのイベントが最終的に処理される限り、状態は少なくとも最終的には正しい状態になります。しかし、これはデータベースの更新と比較して実際に作業するのが非常に難しいため、このように物事を行うことを検討するために多くの並列性が本当に必要になります- お勧めしません


0

免責事項:私はDB:pでかなり初心者です

そうは言っても、データをサテライズするこのアプローチは、パフォーマンスに即座に影響を与えます。

  • グッド主テーブル上の少ない交通
  • グッド主テーブルの上に小さな行
  • 悪い衛星データを必要とするが、別のルックアップが必要であることを意味します
  • 悪いすべてのオブジェクトは、両方のテーブルに存在する場合にはより多くのスペースを占有しました

要件に応じて、これを歓迎する場合もしない場合もありますが、必ず考慮すべき点です。


-1

あなたのスキームが「不変」と呼ばれる方法がわかりません。

補助テーブルに保存されている値が変更されるとどうなりますか?そのテーブルで更新を実行する必要があるようです。

データベースが真に不変であるためには、「INSERTS」だけで保守する必要があります。このためには、「現在の」行を識別する何らかの方法が必要です。これはほぼ常にひどく非効率的です。以前の変更されていない値をすべてコピーするか、クエリ時に複数のレコードから現在の状態をつなぎ合わせる必要があります。現在の行の選択には、通常、(where updTime = (SELECT max(updTime) from myTab where id = ?)のような恐ろしく乱雑なSQLが必要です。

この問題は、DataWarehousingで頻繁に発生し、データの履歴を長期にわたって保持し、特定の時点の状態を選択できるようにする必要があります。通常、ソリューションは「ディメンション」テーブルです。ただし、DWの「昨年1月の営業担当者」の問題は解決しました。Javaの不変クラスが提供する利点はありません。

より哲学的なメモについて。「ステート」(銀行残高、電力消費、StackOverflowでのブラウニーポイントなど)を保存するためのデータベースが存在します。


単一のレコードの場合、WHERE id = {} ORDER BY updTime DESC LIMIT 1通常は非効率的ではありません。
イズカタ

@Izkata-3つのテーブルの結合の中間に入れてみてください:-)
ジェームスアンダーソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.