コンテキスト
私はデータベースを設計しています。これは、単純化して、相互にジョブ要求を送信するユーザーを処理でき、その後、ジョブを開始、終了、および確認できるようにする必要があります。設計はスケーラブルでなければなりません(何百万ものユーザーを考えてください)。
私が検討したアプローチ:
巨大なテーブル
おそらく最良の方法ではないアプローチの1つは、すべてのジョブを1つの巨大なテーブルjobs
に単純に格納することです。このテーブルには、ジョブが現在どの状態にあるかを表すstate
列が必要です(例:ACCEPTED
、STARTED
、FINISHED
、REVIEWED
等)。私が見ることができるこのアプローチの最大の問題は、さまざまな州のジョブに、それらに関連するさまざまなタイプのデータがあることです。たとえば、ジョブリクエストには事前に合意された価格がありますが、ジョブが開始される前に変更され、ジョブが完了する前に再度変更される可能性があります。これはもちろん、テーブルに列を追加して適切に名前を付けるだけで解決できますが、1つのテーブルにさまざまなタイプの考えられるすべてのデータを含むことは、非常に早い段階でパフォーマンスの大きなボトルネックになる可能性があります。仕事の。
州ごとに異なるテーブル
このアプローチでは、複数のテーブル、たとえばjob_requests
、jobs_started
、jobs_finished
などのテーブルを作成し、これらのテーブルにサブステートを含めることができます。 job_requests
にはサブステートPENDING
、ACCEPTED
があり、jobs_finished
テーブルにはサブステートCOMPLETED
、CANCELLED
があります。 、REVIEWED
。
このアプローチでは、各テーブルには現在のジョブの状態に関連するデータのみが含まれますが、一方で、一部のデータが重複する可能性があります(たとえば、ジョブリクエスター、ジョブレシーバーのユーザーID-一方で、この情報はさらに別のテーブルに保存されますか?)。このアプローチの問題は、状態間を遷移するときにすべての情報をアーカイブする方法についての良い解決策を考えることができないことです。たとえば、ジョブリクエストが受け入れられて開始されたら、job_requests
テーブルから削除してjobs_started
テーブルに移動する必要がありますが、利害関係者が希望するのは時間の問題ですたとえば、ジョブリクエストが作成されてから開始されるまでの平均時間を知っています。その時点で、ジョブリクエストを計算できるようにするには、job_requestsテーブルのデータが必要です。
この種の問題は簡単に解決できるように思えますが、「適切だ」と感じる良い解決策は本当に考えられません。思いついた解決策は醜く感じ、すぐに多くのことを考えることができます悪いソリューション。
私が取ることができるアプローチに関するフィードバックやヒントにとても感謝しています。前もって感謝します!
保存しようとしているデータの3つの主要なカテゴリがあるように聞こえます。
重要なのは、イベントのようなデータを他のものすべてから分離することです。
詳細は次のとおりです。
状態固有ではないすべての情報は(たとえば、)「ジョブ」テーブルに入ります。自動生成された主キー:job_id
状態遷移に関するすべての情報は、「job_state_transitions」テーブルに入ります。このテーブルには、次の列が含まれる場合があります。
理想的には、このテーブルは追加専用です。ここで更新または削除されるものはありません。
このようなテーブルを使用すると、job_transitionsテーブルから特定のjob_idの最新の行を選択することにより、特定のジョブの最新のステータスを見つけることができます。さらに非正規化して、「job_state」列を導入できます。その内容は、job_transitionテーブルに新しい行が挿入されるたびに更新されます(ストアードプロシージャが役立つ場合があります)。
タイミングデータが保持されるため、状態遷移に関するあらゆる種類の分析を行うこともできます(created_atは、これに役立つ日付/時刻フィールドです)。
状態固有のデータはすべて「[state] -jobs」テーブルに格納されます。主キー:いくつかのシーケンスID。メインインデックス:job_transition_id
また、「監査合意」テーブルを導入して、「価格合意済みの変更」など、ユーザーが各ジョブに対して要求する可能性のあるさまざまな変更を追跡できるようにすることもできます。これは、状態遷移テーブルの一般化です。イベントを含む1つのメインテーブルと、各イベントタイプごとに1つの補足テーブル(たとえば、job_id、created_at、from_price、to_price列を持つprice_changesテーブル)。
メインの「jobs」テーブルが扱いにくくなる場合は、job_idまたはrequesting_user_idなどでシャーディングできます。
同様に、イベントテーブルは追加専用でなければならず、終了したジョブに関連するイベントをローテーションまたはパージできます。
2つのハイブリッドはどうですか? JobIDと状態(およびおそらくすべてのジョブと状態に共通のその他の情報)を持つ大きなテーブルと、追加の状態情報を管理するためのその他のテーブル。これにより、データの重複が削減(または排除)されますが、管理が容易になります。
この特定のデータには、noSQLデータベースの使用を検討してください。このように、「列」が何であるかに関係なく、object
をレコードに入れることができます。
選択したデータベースシステム(および実装されたソリューション)に応じて、必要に応じて別のデータでレコードを「上書き」できます。もちろん、履歴データを保存して、データベースに新しいレコードをPUT
だけ保存することもできます。
このレコードから始めることができます。
myStartedObject{
id: 1,
state: 'started',
agreedUponPrice: 100.00,
someOtherData: 'x'
}
そして、時間の経過とともに次のように変化します。または、履歴レコードを保持したい場合は、id
が変化します。
myFinishedObject{
id: 1
state: 'finished',
finishedPrice: 209.00,
someFinishedData: 'y'
}
オブジェクト内にすべての州の日付を保存することも、もちろん次のようなオプションです。
myObject
{
id: 1,
states: [{ state: 'started', agreedUponPrice: 100.00, someOtherData: 'x'},
{ state: 'finished', finishedPrice: 209.00, someFinishedData: 'y'}]
}
覚えておいてください、これは確かに特効薬ではありませんが、「スキーマ」が非常に緩やかであり、これらの列のすべてでRDBMSを汚染したくないので、検討する必要があるかもしれません。
また、どのRDBMSでもこれを実行できることに注意してください。 MS SQLでは、XMLデータ型があることは知っています。これにより、このXML列にデータを格納できます。このXML列には、たとえば、逆シリアル化されたオブジェクトを含めることができます。これは「実際の」noSQLソリューションではありませんが、少し似ています。