【问题标题】:key-value store for time series data?时间序列数据的键值存储?
【发布时间】:2010-12-13 14:51:55
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 可惜没有人回答这个问题……我又问了一遍,看看有没有人知道。

标签: database time-series


【解决方案1】:

听起来 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 提问,其他人可能会想出一种更简单的方法来获取最接近时间的事件。

【讨论】:

  • 我不确定您过去是否使用过时间序列,但这种设计无法扩展。
  • @Matthieu N(或其他任何人)愿意详细说明吗?
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    我编写了一个正在积极开发中的开源时间序列数据库(目前仅 .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);
    }
    

    【讨论】:

    • 不知道为什么没有人投票但迟到了,谢谢分享
    【解决方案4】:

    我最近在 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% 的缓存命中率,因此我可以访问任何 pre - 任何交易品种的计算指标集通常在 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
        |]
    

    【讨论】:

      【解决方案5】:

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

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

      然后,在金融领域,我为每根柱线、报价、交易、报价等创建特定的数据结构......

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-03
        • 2023-04-01
        • 2012-09-02
        • 1970-01-01
        • 2016-10-31
        • 1970-01-01
        • 2022-11-15
        相关资源
        最近更新 更多