时间序列数据的键值存储?

时间:2009-11-05 21:38:48

标签: database time-series

我一直在使用SQL Server来存储几十万个对象的历史时间序列数据,每天观察大约100次。我发现查询(给我在时间t1和时间t2之间的对象XYZ的所有值)太慢(对于我的需要,慢是超过一秒)。我按时间戳和对象ID索引。

我接受了使用像MongoDB这样的键值商店的想法,但我不确定这是否是对这种事物的“适当”使用,我找不到任何提及使用这样的数据库来获取时间序列数据。理想情况下,我可以进行以下查询:

  • 在时间t1和时间t2之间检索对象XYZ的所有数据
  • 执行上述操作,但每天返回一个日期点(第一个,最后一个,关闭到时间t ...)
  • 检索特定时间戳的所有对象的所有数据

应该对数据进行排序,理想情况下,编写新数据以及更新现有数据应该很快。

似乎我希望通过对象ID和时间戳查询可能需要以不同的方式索引数据库的两个副本以获得最佳性能...任何人都有建立像这样的系统的经验,有一把钥匙 - 值商店,或HDF5,还是其他什么?或者这在SQL Server中是否完全可行,我只是做得不对?

5 个答案:

答案 0 :(得分:3)

听起来MongoDB非常适合。更新和插入速度非常快,因此您可能希望为每个事件创建文档,例如:

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

然后你可以索引ts字段,查询也会很快。 (顺便说一句,您可以在单个数据库上创建多个索引。)

如何进行三项查询:

  

检索对象XYZ的所有数据   在时间t1和时间t2之间

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

执行上述操作,但返回一个日期   每天一点(第一,最后,关闭到   时间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})

如果您有任何问题,请随时向user list询问,其他人可能会想到一种更简单的方式来获取最接近时间的事件。

答案 1 :(得分:2)

这就是为什么存在特定于时间序列数据的数据库的原因 - 关系数据库对于大型时间序列来说根本不够快。

我在投资银行使用了Fame。它速度非常快,但我认为非常昂贵。但是,如果您的应用程序需要速度,则可能值得查看。

答案 2 :(得分:2)

我写的是一个开源时间序列数据库正在进行的活动开发(目前只有.NET)。它可以以“二进制平面文件”的方式存储大量(terrabytes)的统一数据。所有用法都是面向流的(正向或反向)。我们积极地将其用于我们公司的股票存储和分析。

我不确定这将是您所需要的,但它将允许您获得前两个点 - 从任何系列(每个文件一个系列)获取从t1到t2的值或者只获取一个数据点。 / p>

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);
}

答案 3 :(得分:1)

我最近在F#尝试了类似的东西。我开始使用空格分隔文件中有关符号的1分钟条形格式,该文件大约有80,000个1分钟条形读数。从磁盘加载和解析的代码不到1毫秒。计算文件中每个句点的100分钟SMA的代码是530毫秒。一旦在1ms内计算,我可以从SMA序列中拉出任何我想要的切片。我只是在学习F#所以可能有一些优化方法。请注意,这是在多次测试运行之后,因此它已经在Windows缓存中,但即使从磁盘加载它也不会超过15ms加载。

日期,时间,开放,高,低,关闭,音量 01/03 / 2011,08:00:00,94.38,94.38,93.66,93.66,3800

为了减少重新计算时间,我将整个计算的指标序列保存到一个带有\ n分隔符的单个文件中,并且在Windows文件缓存中加载和解析通常需要不到0.5毫秒。对整个时间序列数据进行简单迭代,以便在3ms的子操作中返回日期范围内的记录集,并且全年为1分钟。我还将每日条形图保存在一个单独的文件中,由于数据量较低,因此加载速度更快。

我使用.net4 System.Runtime.Caching层来缓存预先计算的系列的序列化表示,并使用专用于缓存的几个RAM来获得几乎100%的缓存命中率,这样我就可以访问任何pre - 任何符号的计算指标集通常在1ms以下运行。

从指标中提取我想要的任何数据片段通常不到1毫秒,因此高级查询根本没有意义。使用这种策略,我可以在不到20ms的时间内轻松加载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
    |]

答案 4 :(得分:0)

我使用HDF5作为我的时间序列库。它有许多有效和快速的压缩方式,可以混合和匹配。它可以与许多不同的编程语言一起使用。

我使用boost :: date_time作为时间戳字段。

在金融领域,我然后为每个条形,刻度,交易,报价创建特定的数据结构......

我创建了许多自定义迭代器,并使用标准模板库功能来有效地搜索特定值或基于时间的记录范围。