我一直在尝试设计一种系统,每10秒钟左右为大量(> 200k)的独特设备存储时间相关数据。基本文档结构如下所示:
{
_id: ObjectID('$some_mongo_id'),
start: $start_epoch,
end: $end_epoch
identifier: $some_string,
values: {
'cpu': [
[1, 2, 3, 4, ...],
[1, 2, 3, 4, ...]
]
}
}
上面可能没有很好地解释一些细节:
我们正在使用空值预先分配每个数组的全部,以解决任何填充/移动问题。我相信这是确认的,因为db stats paddingFactor总是像1.000000000029。
值数组是多维的,基于读取和观察,数组实际上更像是mongo内部的链表,因此这减少了需要为给定更新执行的遍历量。结构相当随意,但为了方便想象,基本上索引1是小时,索引2是四分之一,索引3是分钟。
我们每10秒更新一次文档,并使用位置更新查询将新值放入数组中的正确位置。像“$ set:{'values.cpu.2.7':1234}”这样的东西。每个更新仅根据其唯一标识符+开始/结束时间查询单个文档(根据说明进行索引和验证)。
根据文档的大小,我们看到了截然不同的性能。如果文档的长度为一天,则我们的吞吐量为X,并受磁盘IO的限制。如果文档的长度为一小时,则我们的吞吐量约为8倍,并受CPU限制(mongostat观察到的集合锁定)。在这些测试中,所有其他变量保持不变。
我发现大约2年前的this thread似乎与我们所看到的相符,但我找不到更新的东西,而且我的谷歌已经用完了。
我的问题是:
当mongo更新文档时,它是否会弄脏文档所跨越的所有页面,从而要求操作系统刷新整个文件而不是发生更改的页面?这似乎是这样,但我无法正式确认。
是否有更好的方法来构建/执行此操作,以便上述内容不会烧毁我们?使用1小时的文件是可能的,但大约是一天文件的索引空间的24倍,在这种情况下相当大。
提前感谢您的帮助。
技术位:
Mongodb服务器2.4.7 RHEL6 x86_64 python 2.6.6 pymongo 2.5.2-3
答案 0 :(得分:0)
关于问题的第一部分,我真的没有我能给出的答案,并且100%肯定。我会深入挖掘并回复你。可能值得挖掘源代码。
关于第二部分,不确定这是一个确切的答案,但我希望我对此事的看法对你有帮助。
我注意到的第一件事是你可以通过缩写名称来节省一些空间(并使文档略小):
{
_id: ObjectID('$some_mongo_id'),
s: $start_epoch,
e: $end_epoch
i: $some_string,
v: {
'cpu': [
[1, 2, 3, 4, ...],
[1, 2, 3, 4, ...]
]
}
}
如果可以,也可以将'cpu'缩写为'c',依此类推。不确定你是否有很多自由,取决于...名称冲突。我建议的原因应该是显而易见的,对于存储的每个文档,您也将该字符串存储在其中。这也是关于小数据的。
在两个选项之间,我会选择8X性能并受cpu约束。
主要担心的是您需要尽量避免移动文档 。根据您提供的填充统计信息,您的预分配似乎运行良好。
选择使用较小的文件似乎是另一方面的更好选择;如果你需要支付移动文件的费用,你会留下较小的洞,你需要找到较小的空白空间。
但是,我相信更多的更新(因子为24?)会导致每个数据集处理更多(查找文档,锁定,更新,写入,(+刷新?))。在两者之间,可能存在性能的“甜点”,其中磁盘io和cpu很好地一起跳舞。如果你有奢侈品,那值得测试一下。如果这不是真的,8X确实是你能得到的最好的,那么你可能需要开始接受你可能达到一台机器可以采取的极限。
如果CPU是瓶颈,我的建议是升级解决方案以使用shards。 'Identifier'和'start'的组合似乎是分片键的一个很好的候选者。 (请记住,一旦选择了分片键,就无法更改它)。实际上,它将以文档方式以分布式方式为您提供多个可以执行写入的系统。它会给你很大的喘息空间......空间方面,但它也可能会以指数方式增加你的主机。