【问题标题】:MongoDB as a Time Series DatabaseMongoDB 作为时间序列数据库
【发布时间】:2011-11-14 04:31:19
【问题描述】:

我正在尝试将 mongodb 用于时间序列数据库,并想知道是否有人可以建议如何最好地针对该场景进行设置。

时间序列数据与股票价格历史非常相似。我收集了来自不同机器的各种传感器的数据。有数十亿个时间戳的值,我想问以下问题(最好来自数据库而不是应用程序级别):

  1. 对于给定的一组传感器和时间间隔,我想要按时间顺序位于该间隔内的所有时间戳和传感器值。假设所有传感器共享相同的时间戳(它们都是同时采样的)。

  2. 对于给定的一组传感器和时间间隔,我希望按时间顺序位于给定间隔内的每个第 k 个项目(时间戳和相应的传感器值)。

关于如何最好地设置和实现查询的任何建议?

感谢您的建议。

【问题讨论】:

    标签: mongodb time-series


    【解决方案1】:

    显然这是一个老问题,但我在研究 MongoDB 以获取时间序列数据时遇到了这个问题。我认为可能值得分享以下方法来提前分配完整的文档并执行更新操作,而不是新的插入操作。请注意,此方法已记录在 herehere

    假设您每分钟都在存储数据。考虑以下文档结构:

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

    这与标准的关系方法相当。在这种情况下,您会为每个记录的值生成一个文档,这会导致大量插入操作。我们可以做得更好。考虑以下几点:

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

    现在,我们可以编写一个文档,执行 59 次更新。这要好得多,因为更新是原子的,单个写入更小,并且还有其他性能和并发优势。但是,如果我们想在一个文档中存储一整天,而不仅仅是整个小时,该怎么办。然后,这将要求我们遍历 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}
      }
    }
    

    使用这种嵌套方法,我们现在最多只需要步行 24 + 60 即可获得当天的最后一个值。

    如果我们构建文档时所有的值都预先填充了填充,我们可以确定文档不会改变大小,因此不会被移动。

    【讨论】:

    • 数据以 UTC 格式存储,您将如何在上一个示例中查询用户所在时区的数据?例如查询UTC以外时区的日期范围之间的数据。
    • 将数据存储在通用时区(如 UTC)中是明智的。要根据不同的时区进行范围搜索,我会使用 moment.js 将本地时间转换为 UTC。
    • 另外,请参阅此链接:mongodb.com/presentations/…
    • 最终解决方案是这样的? code { timestamp: 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} } } ?或者我需要在文件上使用分钟/小时...?
    • WiredTiger 上市后,这仍然是推荐的方法吗?
    【解决方案2】:

    如果您不需要永久保留数据(即,您不介意它“过期”),您可能需要考虑“封顶集合”。有上限的集合有许多限制,这些限制反过来又提供了一些有趣的好处,听起来它们非常符合您的需求。

    基本上,有上限的集合具有指定的大小,文档按插入顺序写入其中,直到填满为止,此时它会环绕并开始用最新的文档覆盖最旧的文档。您可以对上限集合中的文档执行的更新略有限制 - 即。您无法执行会更改文档大小的更新(因为这意味着需要将其移动到磁盘上才能找到额外的空间)。根据您的描述,我看不出这是个问题。

    结果是您可以保证您的上限集合中的数据将按插入顺序写入并保留在磁盘上,这使得插入顺序查询非常快。

    顺便问一下,传感器及其产生的数据有何不同?如果它们相对相似,我建议将它们全部存储在同一个集合中以方便使用 - 否则将它们分开。

    假设您使用单个集合,那么您的两个查询听起来都非常可行。要记住的一件事是,为了获得封顶集合的好处,您需要根据集合的“自然”顺序进行查询,因此通过时间戳键进行查询不会那么快。如果定期读取读数(因此您知道在给定的时间间隔内将读取多少读数),我建议查询 1 如下所示:

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

    例如,假设您每秒存储 100 个读数,以上将返回最后 100 秒的数据。如果您想要前 100 秒,您可以添加 .skip(100000)

    对于您的第二个查询,在我看来您需要 MapReduce,但听起来并不是特别困难。您可以使用与上述类似的查询来选择您感兴趣的文档范围,然后使用map 函数仅选择您感兴趣的时间间隔内的文档。

    这里是关于上限集合的 Mongo Docs:http://www.mongodb.org/display/DOCS/Capped+Collections

    希望这会有所帮助!

    【讨论】:

    • 谢谢拉塞尔。我们确实希望永远存储数据,这是考虑 mongodb 的原因之一,因为它的可扩展性。传感器相对相似,其中一些具有不同的采样率并由不同的机器记录。这意味着时间戳中存在轻微错位的可能性。因此,不同的传感器可能会存储在单独的集合中。我很想知道mongodb是否自然按记录顺序存储结果?我试图了解一旦我们有大量数据,对特定时间范围的快速查询将如何。
    • (从上面的评论开始)感谢您指导我使用 Map Reduce。乍一看数据,不需要全分辨率,但如果需要,您可以放大。数据将使用 flot 绘制。将绘制窗口上的最大值或平均值,或者可能仅绘制每个第 k 个样本。找到最大值或平均值可能非常慢,所以我想了解查询的速度有多快,看看这是否可行,或者是否会花费太多时间。知道数据是否按顺序存储吗?如果可以快速完成按时间戳而不是 id 的顺序返回结果?
    • 恐怕只有上限集合才能保证按插入顺序存储数据。但是,听起来您将要存储的数据将被写入一次,永远不会更新,可能默认情况下最终会以插入顺序存储 - 尽管我想您最终会得到一个块一个集合,然后是另一个集合的块等。只要您在时间戳列上有索引,您的查询就应该相对较快。请记住,尽管 MapReduce 是一个“慢”操作 - 它的优势在于它可以可靠地(和水平地)扩展。希望这会有所帮助!
    • 谢谢。我只会索引时间戳并容忍 map reduce 的速度。我还打算研究“新聚合框架”来代替 Map reduce。
    【解决方案3】:

    我知道这是一个老问题,但我发现这些博客对我帮助很大:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多