带有 mgo 的 Go (golang) 中的 MongoDB:如何更新记录、确定更新是不是成功并在单个原子操作中获取数据?

Posted

技术标签:

【中文标题】带有 mgo 的 Go (golang) 中的 MongoDB:如何更新记录、确定更新是不是成功并在单个原子操作中获取数据?【英文标题】:MongoDB in Go (golang) with mgo: How do I update a record, find out if update was successful and get the data in a single atomic operation?带有 mgo 的 Go (golang) 中的 MongoDB:如何更新记录、确定更新是否成功并在单个原子操作中获取数据? 【发布时间】:2012-07-10 04:38:26 【问题描述】:

我在 Go 下使用 MongoDB 的 mgo 驱动程序。

我的应用程序请求一个任务(仅在 Mongo 中从名为“jobs”的集合中选择一个记录),然后将自己注册为受让人以完成该任务(对同一“job”记录的更新,将自己设置为受让人)。

该程序将在多台机器上运行,都与同一个 Mongo 通信。当我的程序列出可用任务然后选择一个时,其他实例可能已经获得了该分配,并且当前分配将失败。

如何确定我读取并更新的记录在更新时是否具有特定值(在本例中为受让人)?

我正在尝试获得一项任务,无论是哪一项,所以我认为我应该先选择一个待处理的任务并尝试分配它,以防更新成功。

所以,我的查询应该是这样的:

“从集合 'jobs' 的所有记录中,更新 仅一个 具有 assignee=null 的记录,将我的 ID 设置为受让人。然后,给我该记录 所以我可以胜任这份工作。”

我如何用 Go 的 mgo 驱动程序来表达这一点?

【问题讨论】:

【参考方案1】:

MongoDB 的家伙在官方文档中描述了类似的场景:http://www.mongodb.org/display/DOCS/Atomic+Operations

基本上,您所要做的就是使用assignee=null 获取任何工作。假设你得到了_id=42 的工作。然后,您可以继续在本地修改文档,方法是设置 assignee="worker1.example.com" 并使用选择器 _id=42, assignee=null 和您更新的文档调用 Collection.Update()。如果数据库仍然能够找到与此选择器匹配的文档,它将自动替换该文档。否则你会得到一个 ErrNotFound,表明另一个线程已经认领了这个任务。如果是这种情况,请再试一次。

【讨论】:

执行选择然后更新不是原子的;它执行两次往返! findAndModify 命令将自动执行此操作。此命令的 Mongo 文档在这里:mongodb.org/display/DOCS/findAndModify+Command 在 Go 中执行此操作的 Mgo 文档在这里:go.pkgdoc.org/labix.org/v2/mgo#Query.Apply 对于这个特定的算法,选择+更新不需要是原子的。只要更新本身是原子的(基本上是 CompareAndSwap / CompareExchange 操作),一切都很好。 如果两台机器在第一次查询时选择相同的作业,其中一台将在更新时失败。当然,最终结果可能总是数据库中的数据永远不会变得不一致,但是使用两个单独操作的失败情况会导致很多不必要的来回。这是... findAndModify 命令存在的全部原因。【参考方案2】:

我希望您在所选答案中看到了 cmets,但这种方法是不正确的。进行选择然后更新将导致往返和两台机器并在其中一台更新assignee之前获取相同的作业。您需要改用findAndModify 方法:http://www.mongodb.org/display/DOCS/findAndModify+Command

【讨论】:

实际上,我需要的解决方案是提供的 tux21b。我需要检索所有“选项”,然后选择一个,然后尝试将其分配给自己。如果不成功,我会尝试另一个。 为什么需要选择所有选项?你是说你只需要选择那个没有被占用的(又名assignee == null)? 你是对的。事实证明,我的需求与我写问题时的想法不同,但您的回答更符合问题。 mgo 通过Query.Apply 方法方便地支持 findAndModify 命令。详情请看下面的答案。【参考方案3】:

这是一个老问题,但以防万一有人还在家里看,Query.Apply 方法很好地支持了这一点。它确实运行 findAndModify 命令,如另一个答案中所示,但它方便地隐藏在 Go goodness 后面。

文档中的示例与此处的问题几乎完全匹配:

change := mgo.Change
        Update: bson.M"$inc": bson.M"n": 1,
        ReturnNew: true,

info, err = col.Find(M"_id": id).Apply(change, &doc)
fmt.Println(doc.N)

【讨论】:

以上是关于带有 mgo 的 Go (golang) 中的 MongoDB:如何更新记录、确定更新是不是成功并在单个原子操作中获取数据?的主要内容,如果未能解决你的问题,请参考以下文章

golang 示例Go和MGO示例

golang 示例Go和MGO示例

golang 示例Go和MGO示例

golang 示例Go和MGO示例

MongoDB in Go (golang) with mgo:如何使用逻辑运算符进行查询?

无法使用带有 golang 的 mgo 检索“_id”值