我正在尝试使用MongoDB处理时间序列。社区采用的通用解决方案是使用子文档以不同的粒度级别存储信息(参见 Schema Design for Time Series Data in MongoDB)。
例如,请查看以下文档:
$setOnInsert
该文档由分钟信息编制索引,并包含一个子文档,用于存储每秒的更详细信息。
到目前为止一切顺利。这种方法需要优化才能正常工作:
另一个优化[..]正在为即将到来的时间段预分配所有文件;这永远不会导致现有文档在磁盘上增长或移动。
要实现上述优化,可以使用update
方法上的db.getCollection('aCollection').update(
{
timestamp_minute: ISODate("2013-10-10T23:06:00.000Z"),
type: “memory_used”
},
{
$setOnInsert: { values: {'0': 0, '1': 0, '2': 0}},
$inc: {"values.30": 1}
},
{ upsert: true }
)
属性。
Cannot update 'values' and 'values.30' at the same time
问题是在两个不同的操作中不可能在同一更新中使用相同的字段。以上更新istruction会生成以下错误:
type
此issue上会跟踪此问题。
我的问题是:有没有解决方法?我的前缀是我无法使用任何预先分配空文档的批处理,因为我无法知道索引字段的值先验(在上例中,字段{{1}}的值。
提前致谢。
答案 0 :(得分:5)
我和我的同事找到了解决方法。我们可以称之为三步初始化。
请记住,MongoDB保证了单个文档的操作原子性。考虑到这一点,我们可以通过以下方式运作:
代码看起来应该类似于以下代码段。
// Firt of all, try the update
var result = db.test.update(
{timestamp_minute: ISODate("2013-10-10T23:06:00.000Z"), type: “memory_used”},
{$inc: {"values.39": 1}},
{upsert: false}
);
// If the update do not succeed, then try to insert the document
if (result.nModified === 0) {
try {
db.test.insert(/* Put here the whole document */);
} catch (err) {
console.log(err);
}
// Here we are sure that the document exists.
// Retry to execute the update statement
db.test.update(/* Same update as above */);
}
如果前提条件成立,则上述过程有效:_id
值应从文档中的其他字段派生。在我们的示例中,_id
值为'2013-10-10T23:06:00.000Z-memory_used
。只有使用这种技术,第2点的插入才会正确失败。