在MongoDB中,最大化写入日常日志文档的性能的策略

时间:2011-11-04 14:20:38

标签: mongodb io nosql

我们有一组日志数据,其中集合中的每个文档都由MAC地址和日历日标识。基本上是:

{
  _id: <generated>,
  mac: <string>,
  day: <date>,
  data: [ "value1", "value2" ]
}

每隔五分钟,我们会在当天的文档中向数据数组添加一个新的日志条目。当我们为每个MAC创建一个新文档时,该文档将在午夜UTC结束。

我们注意到,IO(按写入的字节数衡量)会整天增加,然后在UTC午夜降低。这不应该发生,因为日志消息的速率是不变的。我们认为意外行为是由于Mongo移动文档,而不是更新其日志数组。值得一提的是,stats()表明paddingFactor是1.0299999997858227。

几个问题:

  1. 有没有办法确认Mongo是否正在更新或移动?我们在慢查询日志中看到了一些动作,但这似乎是轶事证据。我知道我可以db.setProfilingLevel(2),然后db.system.profile.find(),最后查找"moved:true",但我不确定是否可以在繁忙的生产系统上执行此操作。
  2. 每个文件的大小都是非常可预测和规律的。假设mongo正在做很多动作,那么找出Mongo能够更准确地预测的原因的最佳方法是什么?或者让Mongo更准确地说明一下?假设上面对问题的描述是正确的,那么调整填充因子似乎不会起作用。
  3. 我应该很容易预先确定文档并删除Mongo的任何猜测。 (我知道padding factor文档说我不应该这样做,但我只需要把这个问题放在我身后。)什么是预先编写文档的最佳方法?编写带有垃圾字节数组字段的文档似乎很简单,然后立即从文档中删除该字段,但是有任何我应该注意的问题吗?例如,我可以想象在删除垃圾字段之前必须在服务器上等待写入操作(即执行安全写入)。
  4. 我担心大约在同一时间预先分配所有一天的文档,因为当时这似乎会使磁盘饱和。这是一个有效的问题吗?我应该尝试分摊前一天的预分配费用吗?

3 个答案:

答案 0 :(得分:4)

以下组合似乎会导致写入性能下降:

  1. 日记已开启。
  2. 将附加条目写入构成较大文档大部分的数组
  3. 据推测,I / O已经饱和。改变这些因素似乎可以防止这种情况发生:

    1. 关闭日记功能。请使用更多副本。
    2. 使用较小的文档。请注意,此处的文档大小以字节为单位,而不是文档中任何数组的长度。
    3. 单独文件系统上的日记。
    4. 此外,还有一些其他技巧可以提高写入吞吐量。除了分片之外,我们发现这些改进是增量的,而我们试图解决“这根本不起作用”的问题,但我将它们包括在这里,以防你正在寻找渐进的改进。 10Gen人did some testing and got similar results

      1. 碎片。
      2. 将长数组拆分为多个数组,以使整个结构看起来更像嵌套树。如果您使用一天中的小时作为键,那么每日日志文档将变为:{"0":[...], "1":[...],...,"23":[...]}
      3. 尝试手动预分配。 (这对我们没有帮助.Mongo的填充似乎与宣传的一样。我原来的问题被误导了。)
      4. 尝试不同的--syncdelay值。 (这对我们没有帮助。)
      5. 尝试没有安全写入。 (我们已经在为日志数据执行此操作,并且在许多情况下都不可能。此外,这看起来有点像作弊。)
      6. 你会注意到我在这里复制了10Gen的一些建议,只是为了完整性。希望我准确地做到了!如果他们发布了一本食谱示例,那么我会在这里发布一个链接。

答案 1 :(得分:0)

mongodb将尝试自适应地处理文档,因为它会了解您在一段时间内如何更新文档。有关详细信息,请访问http://www.mongodb.org/display/DOCS/Padding+Factor

如果您发现mongodb在一段时间后仍在移动文档,您可能需要尝试手动填充文档,这样您就不必担心它必须移动文档。

在您的情况下,似乎您应该能够这样做,因为一天中的样本数量是恒定的(对于您的5分钟间隔。)您可以打印db。{yourcollectionname}的输出。 stats()?

关于第4点:你可以按照你提到的那样分摊成本,但我会尝试在你第一次需要时插入文件,看看它是如何运作的,然后尝试其他的东西。

你可以通过探索其他模式来绕过这个特定的问题,但我不确定你们所尝试的是什么。您是否在数组中存储键值对,时间戳是关键? 一个示例修改是移动到类似的东西: {     id:1,     指标:         {           “00:05”:{“metric1”:“value1”},           “00:10”:{“metric2”:“value2”}         } }

答案 2 :(得分:0)

您正在数据数组中执行可预测/恒定的推送次数。 (24 * 60)/ 5 = 288在一天。 我强烈建议在文档中预分配288个元素数组(或者如果您决定每隔3分钟执行一次灵活性和扩展1000),然后相应地为每个数据条目添加更新文档。 这是如何进行的:

- 为每个文档添加1个密钥,这将保留要在关联data数组中更新的密钥编号。例如。最初,文档在第一次插入或刷新时看起来像 数据数组由更新:

{
      _id: <generated>,
      mac: <string>,
      day: <date>,
      data: { "1" : "myGarbageValue","2" : "myGarbageValue",
              "3" : "myGarbageValue"....."1000": "myGarbageValue" }
      n: 1
}

对于每次更新,您必须在等同于data的{​​{1}}密钥上进行快照,并增加n 经过2次数据更新后:

n

优点:

  • 如果您的 { _id: <generated>, mac: <string>, day: <date>, data: { "1" : "myFirstValue","2" : "mySecondValue", "3" : "myGarbageValue"....."1000": "myGarbageValue" } n: 3 } myGarbageValuemyFirstValue在尺寸和格式方面都很简洁,那么最好减少文档的增长。
  • mySecondValue始终会告诉您n数组的当前大小,并允许您运行范围查询以查找data数组大小,这在以前的结构中是不可能的,因为$ size运算符只能返回完全匹配的大小,而不是范围。 http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24size
  • 文档未展开时,Upsert性能更佳。这里是一个基于干净的密钥,例如在data上,而在旧结构中,它是data.23,具有线性插入性能,随着$push数组的增长而变慢。

缺点:

  • 您的数据使用了更多的磁盘空间,因为每24小时刷新一次数据就不会有问题。

希望这些建议有所帮助。尝试一下,让我们所有人知道它是否对你有益。