【问题标题】:MongoDB - field reflecting insertion orderMongoDB - 反映插入顺序的字段
【发布时间】:2016-08-02 12:28:36
【问题描述】:

我有一个应用程序可以不断地将文档插入到 MongoDB 集合中。

我正在寻找一种按照插入顺序查询文档的方法。

我想使用的候选人:

  • _id 字段
  • 创建日期字段
  • 序列号(自动递增)

正如文档所说,_id 字段不是一个好的候选者。创建日期字段可能是一个很好的候选者,但是时钟可能不同步的事实可能会破坏顺序。关于序列号,文档提出了两种方法:计数器和乐观循环。计数器方法不能保证插入顺序,因为即使D1.seq < D2.seq 也可能在另一个文档D2 之后插入文档D1。例如,如果D1 占用序列号 5,则 D2 占用序列号 6,然后插入 D2,然后插入 D1。在繁重的插入环境中,乐观循环方法很疯狂。

还有其他方法吗?


编辑:

使用计数器的方法是有问题的。考虑以下场景。我有一个应用程序A,它不断地将文档插入到集合中。我还有另一个应用程序B,它不断地从同一个集合中轮询文档。应用程序A 是多线程的。两个线程T1T2 即将分别插入文档D1D2。在插入过程中,应用程序B 要求提供更多文档。假设以下操作顺序:

  1. 线程A-T1获取下一个序列号N
  2. 线程A-T2获取下一个序列号N+1
  3. 线程A-T2插入D2
  4. 应用程序B 请求带有seq >= N 的文档(假设最后处理的文档有seq 编号N-1)并接收D2D1 尚未插入)
  5. 线程A-T1 插入D1
  6. 应用程序B 请求带有seq >= N+2 的文档(因为最后处理的文档的序列号为N+1

在这种情况下,D1 将永远不会被处理。

【问题讨论】:

  • 如果我理解正确,您需要一种方法来了解文档的创建顺序和保存顺序。我建议您自己生成_id。例如,当服务器启动时,您会插入最后一个_id(或最大的。如1505)。然后你只需在每个文件上增加一个 istert 计数器。你很高兴。即使某些文档无法保存,您也可以将其保存到某个 JSON 文件中并稍后重新保存。 _id 将在那时生成,您将拥有订单系统。希望这会有所帮助。
  • 我看不出您的方法与问题中的第三种方法有何不同。
  • 是的,只是更多的单词。
  • @MickaelMarrache 我也有同样的问题,你为此选择了什么解决方案?

标签: mongodb


【解决方案1】:

如果您预计每秒有数十次插入,那么乐观锁是唯一的方法。

否则时钟同步可能是一个更好的主意。

考虑到计数器,您能否详细说明它如何影响您的应用程序,如果D1D2 之后持续存在,因为您保证获取序列号的顺序? mongodb 中的“插入”操作本身有多个阶段,您可以深入到依赖日志。

编辑

您会考虑将tailable cursor 作为应用程序B 的一个选项吗?它不直接回答问题,但它可能解决问题背后的问题。

编辑 2

那么您可能需要使用任何类型的消息队列在应用程序之间进行通信,例如在图像上。这可能有点矫枉过正,但如果您确定乐观锁是一个瓶颈,那么它可能是可以接受的。

如下图:

  1. 应用程序A以任意顺序插入文档并从 mongo 客户端检索唯一的对象 ID。

  2. 应用程序A将objectID发送到队列以任意顺序

  3. 应用程序B从队列中获取下一个objectID

  4. 应用程序B从数据库中按ID获取文档

编辑 3

最后,您可以考虑为文档添加状态并将乐观锁转移到应用程序B

  1. 检索未处理文档的objectID:db.collection.findOne({status: null}, {})

  2. 将其状态更改为“处理中”

    db.collection.findAndModify({
        query: { _id: objectId, status: null }, 
        update: { $set: { status: 'processing' }}
    })
    

    如果它返回 null - 文档正在被另一个 B 实例处理,所以返回步骤 1

  3. 处理文档并将其状态更新为“完成”:

    db.collection.findAndModify({
        query: { _id: objectId, status: 'processing' }, 
        update: { $set: { status: 'done' }}
    })
    

使用这种方法,您根本不需要关心确切的顺序。如果您希望按顺序处理文档,您可以添加时间戳,或在 ObjectId 上中继以在步骤 1 中对文档进行排序。当然,它可能不是确切的顺序,但您不需要它来保证所有文件都得到处理。

【讨论】:

  • 关于您对乐观循环的评论,我认为恰恰相反——即乐观循环在大量并发插入时效率低下,因为它可能会循环多次,然后才能插入文档而不会出现重复错误。
  • 乐观锁与时间戳的原因是,当您以毫秒为单位时,NTP 可能不够用,但我看到您指出了性能。尽量避免过早优化,直到您证明这是一个问题。关于您的编辑 - 应用程序 B 如何知道序列号?它完全属于应用程序A 域。 B 只能要求退货,例如最新的 1000 个文档,按序列号排序。假设您添加时间戳进行过滤,任何其他过滤器(例如“比时间戳更早”)也可以。序号仅用于排序。
  • Tailable 游标仅适用于根据文档的上限集合 - 与我无关:(
  • B 不能简单地要求最新的 1000 个文档。它在收到最后一份文件后要求提供更多文件。这里重要的一点是,我不希望将文档插入到 B 已经覆盖的文档范围内。如果插入了此类文档,则 B 将永远无法看到它们。因此,我需要定义一个排序,使得文档按照此顺序插入。
  • 您似乎使用了错误的工具。我已经用可能的解决方案更新了答案。
猜你喜欢
  • 1970-01-01
  • 2015-11-30
  • 2019-07-04
  • 1970-01-01
  • 2019-01-31
  • 1970-01-01
  • 2011-08-15
  • 2018-05-31
  • 2018-03-15
相关资源
最近更新 更多