時系列データを保存するための「最良の方法」は実際には存在せず、正直に多くの要因に依存しています。ただし、主に次の2つの要因に焦点を当てます。
(1)このプロジェクトは、スキーマを最適化するあなたの努力に値するほど深刻ですか?
(2)どのようなクエリアクセスパターンがされている本当にのようになるだろうか?
これらの質問を念頭に置いて、いくつかのスキーマオプションについて説明しましょう。
フラットテーブル
フラットテーブルを使用するオプションは、質問(1)と関係があります。これが深刻なプロジェクトでも大規模なプロジェクトでもない場合は、スキーマについて考えすぎないほうがはるかに簡単です。次のようにフラットテーブルを使用します。
CREATE flat_table(
trip_id integer,
tstamp timestamptz,
speed float,
distance float,
temperature float,
,...);
これがあなたの時間の多くを保証しない小さなプロジェクトである場合にのみ、このコースをお勧めする多くのケースはありません。
寸法と事実
したがって、質問(1)のハードルをクリアし、さらにパフォーマンススキーマが必要な場合、これは最初に検討すべきオプションの1つです。基本的な正規化が含まれますが、測定された「事実」量から「次元」量を抽出します。
基本的に、旅行に関する情報を記録するテーブルが必要です。
CREATE trips(
trip_id integer,
other_info text);
タイムスタンプを記録するテーブル
CREATE tstamps(
tstamp_id integer,
tstamp timestamptz);
最後に、ディメンションテーブルへの外部キー参照(つまり、meas_facts(trip_id)
参照trips(trip_id)
とmeas_facts(tstamp_id)
参照tstamps(tstamp_id)
)を使用して、測定されたすべてのファクト
CREATE meas_facts(
trip_id integer,
tstamp_id integer,
speed float,
distance float,
temperature float,
,...);
これは、最初はそれほど役に立たないように思えるかもしれませんが、たとえば数千の同時旅行がある場合、それらはすべて1秒に1回測定を行っている可能性があります。その場合、tstamps
テーブル内の単一のエントリを使用するのではなく、各旅行のたびにタイムスタンプを再記録する必要があります。
ユースケース:このケースは、データを記録している同時旅行が多く、すべての測定タイプに同時にアクセスすることを気にしない場合に適しています。
Postgresは行ごとに読み取るため、たとえばspeed
特定の時間範囲での測定など、必要にmeas_facts
応じてテーブルから行全体を読み取る必要があります。これにより、作業中のデータセットが大きすぎない場合は、違いに気付かないでしょう。
測定した事実を分割する
最後のセクションをもう少し拡張するには、測定値を別々のテーブルに分割します。たとえば、速度と距離のテーブルを示します。
CREATE speed_facts(
trip_id integer,
tstamp_id integer,
speed float);
そして
CREATE distance_facts(
trip_id integer,
tstamp_id integer,
distance float);
もちろん、これが他の測定にどのように拡張されるかを見ることができます。
使用例:これにより、クエリの速度が大幅に向上することはありません。1つの測定タイプについてクエリを実行する場合、おそらく速度が直線的に増加するだけです。これは、速度に関する情報を検索する場合、speed_facts
表の行に存在する余分な不要な情報ではなく、表から行を読み取るだけでよいためmeas_facts
です。
そのため、1つの測定タイプのみに関する膨大なデータを読み取る必要があり、何らかの利点が得られます。1秒間隔で10時間のデータを提案するケースでは、36,000行しか読み取れないため、これを行っても大きなメリットは得られません。ただし、すべて約10時間である5,000回の旅行の速度測定データを表示する場合、1億8,000万行を読み取ることになります。一度に1つまたは2つの測定タイプのみにアクセスする必要がある限り、このようなクエリの速度を直線的に向上させることで、いくつかの利点が得られます。
Arrays / HStore /およびTOAST
おそらく、この部分について心配する必要はありませんが、重要な場合は知っています。あなたがアクセスする必要がある場合は、巨大な時系列データの量を、そしてあなたが一つの巨大なブロックでそれのすべてにアクセスする必要が知っている、あなたはの使用になります構造を使用することができますTOASTテーブルの圧縮、本質的に大きいのあなたのデータを格納し、セグメント。これにより、すべてのデータにアクセスすることが目標である限り、データへの迅速なアクセスが可能になります。
1つの実装例は
CREATE uber_table(
trip_id integer,
tstart timestamptz,
speed float[],
distance float[],
temperature float[],
,...);
このテーブルでtstart
は、配列の最初のエントリのタイムスタンプが格納され、後続の各エントリは次の秒の読み取り値になります。これには、アプリケーションソフトウェアの各配列値に関連するタイムスタンプを管理する必要があります。
別の可能性は
CREATE uber_table(
trip_id integer,
speed hstore,
distance hstore,
temperature hstore,
,...);
(タイムスタンプ、測定)の(キー、値)ペアとして測定値を追加します。
ユースケース:これはおそらく、PostgreSQLに慣れている人に任せた方がよい実装であり、アクセスパターンがバルクアクセスパターンである必要があると確信している場合に限ります。
結論は?
わあ、これは予想よりずっと長くなった、ごめんなさい。:)
基本的に、多くのオプションがありますが、2番目または3番目を使用することで、より一般的なケースに合うため、おそらく最大の価値が得られます。
PS:最初の質問は、すべてのデータが収集された後、データを一括読み込みすることを意味していました。PostgreSQLインスタンスにデータをストリーミングしている場合、データの取り込みとクエリのワークロードの両方を処理するために、さらに作業を行う必要がありますが、それについては後ほど説明します。;)