13

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

我已经考虑过使用像 MongoDB 这样的键值存储来代替,但我不确定这是否是对这类事物的“适当”使用,而且我找不到任何提及使用这样的时间序列数据的数据库。理想情况下,我可以进行以下查询:

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

数据应该是有序的,理想情况下应该快速写入新数据以及更新现有数据。

似乎我希望通过对象 ID 和时间戳进行查询可能需要以不同的方式对数据库的两个副本进行索引以获得最佳性能......任何人都有构建这样一个具有键值存储的系统的经验,或HDF5,或别的什么?或者这在 SQL Server 中完全可行,而我只是做得不对?

4

5 回答 5

3

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

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

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

如何做你的三个查询:

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

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

如果您有任何问题,请随时在用户列表中提问,其他人可能会想出一种更简单的方法来获取最接近时间的事件。

于 2009-12-30T01:45:19.927 回答
2

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

我不确定这是否正是您所需要的,但它可以让您获得前两点 - 为任何系列(每个文件一个系列)获取从 t1 到 t2 的值,或者只获取一个数据点。

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 回答
2

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

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

于 2009-11-05T21:47:53.933 回答
1

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

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

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

我使用 .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 回答