2011-11-04 61 views
13

我们有一组日志数据,其中集合中的每个文档都由MAC地址和日历日标识。基本上:在MongoDB中,用于最大化写入日常日志文档性能的策略

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

每五分钟,我们在当天的文档中追加一个新的日志条目到数据数组中。当我们为每个MAC创建一个新文档时,该文档将在UTC午夜翻阅。

我们已经注意到,按写入的字节数衡量,IO整天增加,然后在UTC的午夜时降低。这不应该发生,因为日志消息的速率是不变的。我们认为这种意外行为是由于Mongo移动文档引起的,而不是更新他们的日志阵列。对于它的价值,stats()显示paddingFactor为1.0299999997858227。

几个问题:

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

Scott Hernandez在Google Group上回答了这个问题,所以我在他的回答中发布了一个列表:http://groups.google.com/group/mongodb-user/browse_thread/thread/8a24e7f3faf95f71# – jtoberon

+0

最新更新:我们仍在试图弄清楚发生了什么。 – jtoberon

回答

4

以下组合似乎导致写入性能跌落悬崖:

  1. 日记上。
  2. 写操作追加条目的阵列,构成了本体大概I/O变为饱和较大文档

的。改变这些因素似乎可以防止这种情况发生:

  1. 打开日志关闭。改用更多副本。
  2. 使用较小的文档。请注意,此处的文档大小以字节为单位,而不是文档中任何数组的长度。
  3. 独立文件系统上的日志。

另外,这里还有一些提高写吞吐量的技巧。除了分片之外,我们发现这些改进是渐进式的,而我们试图解决“这根本不起作用”的问题,但是我将它们包括在内以防您正在寻求渐进式改进。 10Gen伙计did some testing and got similar results

  1. 碎片。
  2. 将长数组分解为几个数组,以便您的整体结构看起来更像是嵌套树。如果您使用当天的小时作为密钥,则每日日志文档将变为:
    {"0":[...], "1":[...],...,"23":[...]}
  3. 尝试手动预分配。 (这对我们没有帮助,Mongo的填充似乎像广告一样工作,我的原始问题被误导了)
  4. 尝试使用不同的--syncdelay值。 (这对我们没有帮助。)
  5. 尝试不安全写入。 (我们已经这样做了日志数据,在很多情况下这是不可能的,而且这似乎有点欺骗)。

你会注意到我已经复制了一些建议从10Gen在这里,只是为了完整。希望我准确地做到了!如果他们发布一个食谱示例,那么我会在这里发布一个链接。

0

mongodb会尝试自适应地预设文档,因为它会了解您在一段时间内如何更新文档。更多信息可以在http://www.mongodb.org/display/DOCS/Padding+Factor

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

在你的情况下,它好像你应该能够做到这一点,鉴于样本在一天的数量是恒定的(你5分钟时间。)你可以打印从数据库输出。{yourcollectionname} .stats()?

关于第4点:您可以按照您所提到的分摊费用,但是我会在第一次尝试插入文件时首次查看文件的执行情况,然后尝试其他方法。

你也许能够通过探索其他模式来绕过这个特定的问题,但我不确定你所尝试的是什么。你是否在数组中存储了键值对,时间戳是关键? 一个例子的修改是将移动到类似: { ID:1, 指标: { “00:05”:{ “metric1”: “VALUE1”}, “00:10”:{“度量值2“:”值2“} } }

+0

我知道适应性预测,但如果它工作,那么我不认为我们会看到我描述的IO模式。我会将这些统计数据添加到问题中。是的,我想手动填充文档;看到我的问题3.你能提供有关如何这样做的细节?是的,我们正在尝试,但正如我所说,我期望遇到问题,所以有经验的人的回答会更有帮助。我不认为架构是这个问题的核心,因为每个写都只是将值添加到数组字段的末尾,但我为这个问题添加了一个示例以防万一。 – jtoberon

+0

mongodb为您的收藏计算的填充因子是什么? – Shekhar

+0

问题:1.0299999997858227 – jtoberon

0

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

- 为每个文档增加1个更多的密钥,这将保持在关联data数组中更新的密钥号码。例如。最初,该文件将看起来像第一次插入或 数据阵列的刷新按更新时间:

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

每次更新时,你必须做data键等于n一个UPSERT,并增加n 2个的更新 后数据:

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

优点:

    的文件
  • 少增长,将b e如果您的myGarbageValuemyFirstValuemySecondValue在尺寸和格式上都是合适的。
  • n总是告诉你你的data阵列的电流的大小,并允许您运行范围查询发现data数组的大小,这是不可能在你以前的stucture为$大小运营商只能返回完全一致的尺寸,而不是范围。 http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24size
  • 当文档不展开时,插入性能会更好。这是一个基于upsert的干净密钥,例如data.23,而在旧结构中,它是一个$push,它具有线性插入性能,并且随着data阵列的增长而变慢。

缺点:

  • 更多的磁盘空间使用你的数据,如您刷新数据,每24小时不应该是一个问题。

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

+0

您是否知道“推送具有线性插入性能”信息的官方来源?我知道这个测试http://blog.axant.it/archives/236,但它只声称“可能”。我们将尝试关联数组的建议,但如果这能起作用,我会感到惊讶。我们远远低于所提到的5000门槛。此外,我不能想到为什么线性插入算法会转化为我们看到的物理IO行为的解释,因为它不可能移动每个条目。 – jtoberon

+0

嗨,jtoberon,没有正式记录,但它在一些基准测试过程中被观察到。但是对于像你这样的小型288阵列来说,这并没有太大的区别。 你是对的,IO差异不是由插入/更新算法差异所决定的,它是由于由于在关联数组中预先分配元素而导致的,你的文档对象不会增长。因此,由mongodb完成的运动更少,少IO。 – DhruvPathak

+0

关联数组的更改没有帮助。 IO负载平滑了,但是在比我们使用正常数组和'$ push'时看到的峰值更差的水平。 – jtoberon