13

私はSQLServerを使用して、1日に約100回観測された、数十万のオブジェクトの履歴時系列データを保存してきました。クエリ(時間t1と時間t2の間のオブジェクトXYZのすべての値を教えてください)が遅すぎることがわかりました(私のニーズでは、遅いのは1秒以上です)。タイムスタンプとオブジェクトIDでインデックスを作成しています。

代わりにMongoDBのようなKey-Valueストアを使用することを考えましたが、これがこの種の「適切な」使用であるかどうかはわかりません。また、そのようなものを使用することについての言及は見つかりませんでした。時系列データのデータベース。理想的には、次のクエリを実行できます。

  • 時間t1と時間t2の間のオブジェクトXYZのすべてのデータを取得します
  • 上記を実行しますが、1日あたり1つの日付ポイントを返します(最初、最後、時間tにクローズ...)
  • 特定のタイムスタンプのすべてのオブジェクトのすべてのデータを取得する

データは順序付けする必要があり、理想的には、既存のデータを更新するだけでなく、新しいデータをすばやく書き込む必要があります。

オブジェクトIDとタイムスタンプでクエリを実行するには、最適なパフォーマンスを得るために、データベースの2つのコピーに異なる方法でインデックスを付ける必要があるようです...誰もがこのようなシステムを構築した経験があり、Key-Valueストアを使用しています、またはHDF5、または他の何か?または、これはSQL Serverで完全に実行可能であり、私はそれを正しく実行していませんか?

4

5 に答える 5

3

MongoDBが非常に適しているようです。更新と挿入は非常に高速であるため、次のようなすべてのイベントのドキュメントを作成することをお勧めします。

{
   object: XYZ,
   ts : new Date()
}

次に、tsフィールドにインデックスを付けることができ、クエリも高速になります。(ちなみに、1つのデータベースに複数のインデックスを作成できます。)

3つのクエリを実行する方法:

時間t1と時間t2の間のオブジェクトXYZのすべてのデータを取得します

db.data.find({object : XYZ, ts : {$gt : t1, $lt : t2}})

上記を実行しますが、1日あたり1つの日付ポイントを返します(最初、最後、時間tにクローズ...)

// first
db.data.find({object : XYZ, ts : {$gt : new Date(/* start of day */)}}).sort({ts : 1}).limit(1)
// last
db.data.find({object : XYZ, ts : {$lt : new Date(/* end of day */)}}).sort({ts : -1}).limit(1)

しばらくの間、カスタムJavaScript関数が必要になる可能性がありますが、それは実行可能です。

特定のタイムスタンプのすべてのオブジェクトのすべてのデータを取得する

db.data.find({ts : timestamp})

ご不明な点がございましたら、ユーザーリストでお気軽にお問い合わせください。他の誰かが、時間に最も近いイベントを取得するためのより簡単な方法を考えられる可能性があります。

于 2009-12-30T01:45:19.927 に答える
2

これが、時系列データに固有のデータベースが存在する理由です。リレーショナルデータベースは、大きな時系列に対して十分な速度ではありません。

私は投資銀行でかなり多くの名声を使用しました。とても速いですが、とても高いと思います。ただし、アプリケーションで速度が必要な場合は、一見の価値があります。

于 2009-11-05T21:47:53.933 に答える
2

私が書いたオープンソースの時系列データベースが活発に開発されています(今のところ.NETのみ)。大量(テラバイト)の均一なデータを「バイナリフラットファイル」方式で保存できます。すべての使用法はストリーム指向(順方向または逆方向)です。当社では、ダニの保管・分析に積極的に活用しています。

これが正確に必要なものになるかどうかはわかりませんが、最初の2つのポイントを取得できます。任意のシリーズ(ファイルごとに1つのシリーズ)のt1からt2までの値を取得するか、1つのデータポイントを取得します。

https://code.google.com/p/timeseriesdb/

// Create a new file for MyStruct data.
// Use BinCompressedFile<,> for compressed storage of deltas
using (var file = new BinSeriesFile<UtcDateTime, MyStruct>("data.bts"))
{
   file.UniqueIndexes = true; // enforces index uniqueness
   file.InitializeNewFile(); // create file and write header
   file.AppendData(data); // append data (stream of ArraySegment<>)
}

// Read needed data.
using (var file = (IEnumerableFeed<UtcDateTime, MyStrut>) BinaryFile.Open("data.bts", false))
{
    // Enumerate one item at a time maxitum 10 items starting at 2011-1-1
    // (can also get one segment at a time with StreamSegments)
    foreach (var val in file.Stream(new UtcDateTime(2011,1,1), maxItemCount = 10)
        Console.WriteLine(val);
}
于 2012-03-13T14:18:06.197 に答える
1

最近、F#で似たようなものを試しました。私は、スペースで区切られたファイル内の問題のシンボルの1分のバー形式から始めました。このファイルには、約80,000の1分のバーの読み取り値があります。ディスクからロードして解析するコードは1ミリ秒未満でした。ファイル内のすべての期間の100分のSMAを計算するコードは530msでした。1ms未満で計算されると、SMAシーケンスから必要なスライスをプルできます。私はF#を学んでいるだけなので、おそらく最適化する方法があります。これは複数のテスト実行後のものであるため、すでにWindowsキャッシュにありますが、ディスクからロードされた場合でも、ロードに15ミリ秒を超えることはありません。

日付、時刻、オープン、高、低、クローズ、ボリューム01/03 / 2011,08:00:00,94.38,94.38,93.66,93.66,3800

再計算時間を短縮するために、計算されたインジケーターシーケンス全体を\ n区切り文字を使用して単一のファイルでディスクに保存します。通常、Windowsファイルキャッシュにある場合、ロードと解析にかかる時間は0.5ミリ秒未満です。完全な時系列データ全体にわたる単純な反復により、1年間の1分バーを使用して、3ミリ秒未満の操作で日付範囲内のレコードのセットを返します。また、データ量が少ないため、ロードがさらに高速になる別のファイルに日次バーを保持しています。

.net4 System.Runtime.Cachingレイヤーを使用して、事前に計算されたシリーズのシリアル化された表現をキャッシュし、キャッシュ専用のRAMを数ギガ使用すると、ほぼ100%のキャッシュヒット率が得られるため、事前に計算されたインジケーターにアクセスできますシンボルに設定すると、通常1ミリ秒未満で実行されます。

インジケーターから必要なデータのスライスを取得するのは通常1ミリ秒未満であるため、高度なクエリは単純に意味がありません。この戦略を使用すると、20ミリ秒未満で10年間の1分のバーを簡単にロードできました。

// Parse a \n delimited file into RAM then 
// then split each line on space to into a
// array of tokens.  Return the entire array
// as string[][]
let readSpaceDelimFile fname = 
    System.IO.File.ReadAllLines(fname)
    |> Array.map (fun line -> line.Split [|' '|])

// Based on a two dimensional array 
// pull out a single column for bar 
// close and convert every value 
// for every row to a float
// and return the array of floats. 
let GetArrClose(tarr : string[][])  =
    [| for aLine in tarr do
         //printfn "aLine=%A" aLine
         let closep = float(aLine.[5])
         yield closep
    |]
于 2011-09-12T22:46:07.323 に答える
0

時系列リポジトリとしてHDF5を使用しています。効果的で高速な圧縮スタイルがいくつかあり、それらを組み合わせて組み合わせることができます。さまざまなプログラミング言語で使用できます。

タイムスタンプフィールドにboost::date_timeを使用します。

次に、金融の領域で、バー、ティック、トレード、クォート、...のそれぞれに特定のデータ構造を作成します。

いくつかのカスタムイテレータを作成し、標準のテンプレートライブラリ機能を使用して、特定の値または時間ベースのレコードの範囲を効率的に検索できるようにしました。

于 2013-11-24T12:38:20.647 に答える