時系列データ用のデータストレージを開発するように求められましたが、重要な研究にもかかわらず、選択したデータモデルとストレージテクノロジーについて確信が持てません。
データストレージに保存されるソースデータは、物理測定ユニットによって提供されます。各ステーションは、測定ステーションごとに最大300の変数(燃料タイプ、燃料消費量、速度など)の異なる変数のサブセットを持つ場合と持たない場合がありますが、すべてのステーションにわたる異なる信号の数は約1500です。ステーションごとに予測される変数のサブセットは事前にわかっています。ただし、時間の経過とともにセンサーがステーションに追加される場合があります(スキーマの変更が時間の経過とともに必要になる場合があります)。すべてのステーションは、20Hzから0.2Hzの範囲のさまざまなレートでデータを提供します。
さらに、これらのすべての測定ステーションで使用できるかなりの量のメタデータがあり、最終的には約500になります。
データは通常、「リアルタイム」ストリームとしてではなく、バッチで提供されます。バッチサイズは、毎時のバッチと毎月のバッチで異なります。
データのクエリは、2つの主な理由で行われます。1つの測定ステーションのデータのレポート作成と統計分析、およびステーション間の比較です。クエリの約80%は、過去30日間に入力されたデータに関連しています。クエリは毎日実行されるため、SELECT
負荷はINSERT
負荷を超えます。
理想的には
SELECT var1, var2, ... varN FROM station_data WHERE station_id=X OR station_id=Y AND TIMESTAMP BETWEEN ... AND ...;
sQL専門家以外のユーザーがデータに簡単にアクセスできるようになります。さらに、単純な時間ベースの集計演算が可能である必要があります(AVG、MAXなどpp)。
現在、高度に正規化された構造は、PostgreSQLデータベースにデータを格納するために使用されています。PostgreSQLデータベースには、変数ごとに1つのテーブルがあり、現在では約6TBに成長しています。約1500のデータテーブルはそれぞれ次の形式です。
(timestamp, station_id, value)
(station_id), (station_id, timestamp), (timestamp)
のインデックスと(station_id, timestamp, value)
の一意の制約。
この構造は、重い外部結合(最大300の外部結合)を必要とするため、データの取得が面倒で計算コストが高くなります。
これまでのところ、次の考慮事項が行われました。
これまでのところ、原理的に機能する2つの異なるスキーマが完成しました。ただし、どちらにも重大な欠点があり、回避策を見つける必要があります。
station_id
でのシャーディングとtimestamp
での毎月のパーティション分割を伴う1つの巨大な垂直データテーブルを持つEAV(アンチ)パターン。必要なスキーマの柔軟性は与えられますが、このパターンは内部結合に大きく依存しているため、必要なアクセスの容易さには適合しません。さらに、さまざまなデータ型の型安全性はdb側では保証されておらず、アクセス制御は不可能です。station_id
ごとに1つのテーブル。この非正規化された構造は、アプリケーションの観点から魅力的に最初に見えます(高速な挿入、インデックス作成がほとんど不要、単一ステーションでの単純なクエリ)。ただし、エンドユーザーが特定のステーションのテーブル名を知らない可能性があるため、クエリには動的SQLが必要であり、ステーション間の比較は拡張SQLクエリまたはクライアント側コードでのみ可能です。ストレージ容量は問題ではありませんが、データ取得の信頼性、アップタイム、スピードが重要です。
スケーラビリティを維持しながら要件を満たすために、推奨されるデータモデルのどれが適していますか?要件に適合する追加スキーマの提案は大歓迎です。
ありがとうございました。
変数の数の変動性を除いて、データに関してはかなり似た状況がありましたが、 TmTron として、JSONがうまくいくかもしれないと述べました。これが私が持っていたスキーマです(あなたのデータに適応しています):
表「センサー:必要なメタデータがすべて含まれています。場合によっては1k +行が定期的に含まれています。7k+実際の違いはありません。
テーブル「sensor_data」:
のようなクエリのトン
_{select timestamp, var1,var2,var3,var4,var5 from sensor_data where sensor_id = xx and timestamp between xxxx and xxxx}
_
テーブルが大きくなり、クエリが遅くなり、顧客の怒りが増しました。
最適化の最初の試みは、sensor_idの範囲によるパーティション分割でした-パーティションあたり20、スペース消費量は同じまま、スキーマはより複雑になり、クエリは高速になりますが、それほど多くはありませんでした。
だから、ここにまだ作業スキーマがあります:
カスタムデータ型「メトリック」(timestamp、var1、var2、var3、var4、var5)
表sensor_data:
selectクエリは、関数get_data(sensor_id、measurement_id、from_time、to_time)select(unnest(dataset))。
挿入はより複雑になりました:
_insert into sensor_data value (to_date(timestamp), sensor, measurement, [(timestamp, var1,var2,var3,var4,var5)])
on conflict (date, sensor_id, measurement_id) do update
set dataset=dataset||excluded.dataset
_
スペース消費量は約10分の1になり、クエリはより複雑になりますが、劇的に高速になります。
Measurement_idでデータをリクエストしない場合は、インデックスとクエリから削除してください。 1日あたりのデータが大幅に多い場合は、「日付」列を「時間」に置き換えて1時間あたりのデータをdate_trunc('hour',timestamp)
として保存し、1か月あたりのパーティションテーブルを保存できるため、最大744(31 * 24)行になります。センサーごと、各テーブルの測定ごと。それはかなり合理的な行数であり、十分に速く機能します。
当然、独自のデータ型を作成する必要があります(ほとんどの場合、型(timestamp、JSON)が機能します)
主なアイデアは、postgresがテーブルの外にデータの配列を保存し、必要なときにだけ(さらには圧縮されて)読み取ることです。そのため、テーブルは別の場所に格納されたデータの「ちょっとしたインデックス」になりましたが、インデックスを付けて分割できるテーブルのままです。
制限は、制約のあるデータセット配列のコンテンツを制御したり、データを直接集計したりできないことです。ただし、単純な集計(max、min、avgなど)の場合は、データを事前に集計して、それを行レベルで保存できます。