MongoDB 集合更新:使用默认值初始化文档

6
我将尝试使用MongoDB处理时间序列。社区通常采用的解决方案是使用子文档在不同粒度级别上存储信息(请参见MongoDB中的时间序列数据架构设计)。例如,看下面的文档:
{
  timestamp_minute: ISODate("2013-10-10T23:06:00.000Z"),
  type: “memory_used”,
  values: [
    999999,   // 1 second
    …
    1000000,  // nth second
    1500000,  // n+1th second
    … 
    2000000   // 60th
  ]
}

这份文档被以分钟信息为索引,并包含一个子文档,用于存储每秒更详细的信息。
到目前为止还不错。这种方法需要进行优化才能正常工作:

另一种优化方法是预分配即将到来时间段的所有文档; 这永远不会导致现有文档在磁盘上增长或移动。

要实现以上优化,可以在更新方法中使用$setOnInsert属性。
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

这个问题在这个issue上进行了追踪。
我的问题是:有没有解决方法?我不想使用预分配空文档的批处理,因为我无法提前知道索引字段type的值(就像上面的例子一样)。谢谢。
1个回答

6
我和我的同事找到了一种解决方法,我们称之为“三步初始化”。
请记住,MongoDB保证对单个文档的操作具有原子性。有了这个事实,我们可以按照以下方式操作:
1. 尝试更新文档,在指定的时间块中适当地增加计数器。不要执行任何upsert操作,只需进行传统的更新操作。请记住,执行update语句返回写入的文档数量。如果写入的文档数量大于零,则完成。
2. 如果更新所写入的文档数量为零,则意味着要更新的相关文档尚未存在于集合中。尝试为指定标记插入整个文档。将所有计数器(字段值)设置为零。同样,执行插入语句返回写入的文档数量。如果返回零或抛出异常,则无论如何都可以:它意味着其他进程已经为相同的标记插入了文档。
3. 再次执行上述更新操作。
代码应该类似于以下代码片段。
// 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处的插入才会正确失败。

1
插入失败的原因是什么?您在类型和时间戳上是否有唯一索引? - Asya Kamsky
你说得对,我忘记插入这个点了。_id 应该从文档中的其他字段派生,例如使用“-”字符将它们连接起来。 - riccardo.cardin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接