web-dev-qa-db-ja.com

時系列データベースとしてのMongoDB

私は時系列データベースにmongodbを使用しようとしていますが、そのシナリオに最適な設定方法を誰かが提案できるかどうか疑問に思っていました。

時系列データは株価履歴と非常によく似ています。さまざまなマシンから取得したさまざまなセンサーからのデータのコレクションがあります。数十億のタイムスタンプに値があり、次の質問をしたいと思います(できれば、アプリケーションレベルではなくデータベースから)。

  1. 特定のセンサーと時間間隔のセットについて、その間隔内にあるすべてのタイムスタンプとセンサー値を時間順に並べたいと思います。すべてのセンサーが同じタイムスタンプを共有していると仮定します(それらはすべて同時にサンプリングされました)。

  2. 与えられたセンサーのセットと時間間隔について、与えられた間隔内にあるすべてのk番目のアイテム(タイムスタンプと対応するセンサー値)が時間順に必要です。

これを最適に設定してクエリを実行する方法に関する推奨事項はありますか?

提案をありがとう。

24
sequoia

データを永久に保持する必要がない場合(つまり、「エージングアウト」してもかまわない場合)は、「上限付きコレクション」を検討することをお勧めします。上限のあるコレクションにはいくつかの制限があり、それらはあなたが望むものに非常によく合うように聞こえるいくつかの興味深い利点を提供します。

基本的に、上限付きコレクションには指定されたサイズがあり、ドキュメントはいっぱいになるまで挿入順に書き込まれます。いっぱいになると、コレクションはラップアラウンドし、最も古いドキュメントを最新のドキュメントで上書きし始めます。上限付きコレクション内のドキュメントに対して実行できる更新には、わずかに制限があります。ドキュメントのサイズを変更する更新を実行することはできません(これは、余分なスペースを見つけるためにディスク上で移動する必要があることを意味します)。これがあなたの説明の問題だとは思えません。

結果として、上限付きコレクション内のデータが挿入順序でディスクに書き込まれ、ディスク上にとどまることが保証されます。これにより、挿入順序でのクエリが非常に高速になります。

ちなみに、センサーとそれらが生成するデータはどのように異なりますか?それらが比較的類似している場合は、使いやすさのためにすべて同じコレクションに保存することをお勧めします。それ以外の場合は、分割します。

単一のコレクションを使用すると仮定すると、両方のクエリは非常に実行可能に聞こえます。覚えておくべきことの1つは、上限付きコレクションを利用するには、コレクションの「自然な」順序に従ってクエリを実行する必要があるため、タイムスタンプキーによるクエリはそれほど高速ではないということです。読み取り値が定期的に取得される場合(つまり、特定の時間間隔で取得される読み取り値の数がわかっている場合)、クエリ1に対して次のようなものを提案します。

_db.myCollection.find().limit(100000).sort({ $natural : -1 })
_

たとえば、1秒間に100回の読み取り値を保存すると仮定すると、上記では最後の100秒分のデータが返されます。前の100秒が必要な場合は、.skip(100000)を追加できます。

2番目のクエリでは、MapReduceが必要になるように思えますが、特に難しいとは思えません。上記と同様のクエリを使用して、関心のあるドキュメントの範囲を選択し、map関数を使用して関心のある間隔でドキュメントのみを選択できます。

キャップされたコレクションに関するMongoドキュメントは次のとおりです。 http://www.mongodb.org/display/DOCS/Capped+Collections

お役に立てれば!

13
Russell

明らかにこれは古い質問ですが、時系列データについてMongoDBを調査しているときに遭遇しました。新しい挿入操作ではなく、事前に完全なドキュメントを割り当てて更新操作を実行するために、次のアプローチを共有する価値があるのではないかと思いました。このアプローチは文書化されていることに注意してください ここ および ここ

毎分データを保存していると想像してください。次のドキュメント構造を検討してください。

{
  timestamp: ISODate("2013-10-10T23:06:37.000Z"),
  type: ”spot_EURUSD”,
  value: 1.2345
},
{
  timestamp: ISODate("2013-10-10T23:06:38.000Z"),
  type: ”spot_EURUSD”,
  value: 1.2346
}

これは、標準的なリレーショナルアプローチに匹敵します。この場合、記録された値ごとに1つのドキュメントを作成するため、多くの挿入操作が発生します。私たちはもっとうまくやれる。次のことを考慮してください。

{
  timestamp_minute: ISODate("2013-10-10T23:06:00.000Z"),
  type: “spot_EURUSD”,
  values: {
    0: 1.2345,
    …  
    37: 1.2346,
    38: 1.2347,
    … 
    59: 1.2343
  }
}

これで、1つのドキュメントを記述し、59の更新を実行できます。更新はアトミックであり、個々の書き込みは小さく、その他のパフォーマンスと同時実行の利点があるため、これははるかに優れています。しかし、1時間だけでなく、1日全体を1つのドキュメントに保存したい場合はどうでしょうか。これには、最後の値を取得するために1440エントリに沿って歩く必要があります。これを改善するために、さらに次のように拡張できます。

{
  timestamp_hour: ISODate("2013-10-10T23:00:00.000Z"),
  type: “spot_EURUSD”,
  values: {
    0: { 0: 1.2343, 1: 1.2343, …, 59: 1.2343},
    1: { 0: 1.2343, 1: 1.2343, …, 59: 1.2343},
    …,
    22: { 0: 1.2343, 1: 1.2343, …, 59: 1.2343},
    23: { 0: 1.2343, 1: 1.2343, …, 59: 1.2343}
  }
}

このネストされたアプローチを使用すると、1日の最後の値を取得するために、最大で24 +60を歩くだけで済みます。

事前にすべての値をパディングで埋めてドキュメントを作成しておけば、ドキュメントのサイズが変更されないため、移動されないことが保証されます。

18
jtromans