Go中的MongoDB(golang)和mgo:如何更新记录,找出更新是否成功,并在单个原子操作中获取数据?
我正在使用mgo驱动程序进行MongoDB下围棋 我的应用程序要求执行一项任务(在Mongo中只需从一个名为“jobs”的集合中选择一条记录),然后将自己注册为受让人以完成该任务(更新同一条“job”记录,将自己设置为受让人) 该程序将在多台机器上运行,所有机器都与同一个Mongo对话。当我的程序列出可用任务,然后选择一个时,其他实例可能已经获得该分配,而当前分配将失败 如何确保我读取并随后更新的记录在更新时是否具有特定值(在本例中为受让人) 我试图得到一个任务,无论是哪一个,所以我认为我应该首先选择一个挂起的任务,并尝试分配它,保持它只是在更新成功的情况下 因此,我的查询应该是这样的: “从集合“作业”上的所有记录中,仅更新一个assignee=null的记录,将我的ID设置为assignee。然后,将该记录提供给我,以便我可以运行作业。”Go中的MongoDB(golang)和mgo:如何更新记录,找出更新是否成功,并在单个原子操作中获取数据?,mongodb,go,mgo,Mongodb,Go,Mgo,我正在使用mgo驱动程序进行MongoDB下围棋 我的应用程序要求执行一项任务(在Mongo中只需从一个名为“jobs”的集合中选择一条记录),然后将自己注册为受让人以完成该任务(更新同一条“job”记录,将自己设置为受让人) 该程序将在多台机器上运行,所有机器都与同一个Mongo对话。当我的程序列出可用任务,然后选择一个时,其他实例可能已经获得该分配,而当前分配将失败 如何确保我读取并随后更新的记录在更新时是否具有特定值(在本例中为受让人) 我试图得到一个任务,无论是哪一个,所以我认为我应该首
我如何用mgo driver for Go来表达这一点?MongoDB的人在官方文档中描述了类似的场景:
基本上,您所要做的就是使用
assignment=null
获取任何作业。让我们假设您获得了返回\u id=42
的作业。然后,通过设置assignee=“worker1.example.com”
并使用选择器调用{u id=42,assignee=null}
和更新的文档,您可以继续在本地修改文档。如果数据库仍然能够找到与此选择器匹配的文档,它将自动替换该文档。否则,您将得到一个ErrNotFound,表示另一个线程已经声明了该任务。如果是这样的话,请再试一次。我希望您看到了对所选答案的评论,但这种方法是不正确的。执行选择然后更新将导致一次往返,两台机器在其中一台机器可以更新受让人之前将获取相同的作业。您需要使用查找并修改方法:这是一个老问题,但万一有人仍在家中观看,该方法很好地支持了这一点。它确实运行findanddomify
命令,如另一个答案所示,但它方便地隐藏在Go Goods后面
文档中的示例与此处的问题非常吻合:
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)
在更新之后执行选择不是原子的;它执行两次往返!findAndModify命令将以原子方式执行此操作。此命令的Mongo文档在这里:在Go中执行此操作的Mgo文档在这里:对于此特定算法,select+更新不需要是原子的。只要更新本身是原子的(基本上是一个CompareAndSwap/compareeexchange操作),一切都很好。如果两台计算机在第一次查询中选择了相同的作业,其中一台将在更新时失败。当然,最终的结果可能总是数据库中的数据永远不会变得不一致,但是使用两个单独操作的失败案例会导致大量不必要的来回。这是。。。findAndModify命令存在的全部原因。实际上,我需要的解决方案是tux21b提供的解决方案。我需要重新查看所有“选项”,然后选择一个,然后尝试将其分配给我自己。如果不成功,我会尝试另一个。为什么需要选择所有选项?你是说你只需要选择一个没有被接受的(aka assignment==null)?你是对的。事实证明,我的需求与我写问题时的想法不同,但您的答案更符合问题。mgo通过该方法方便地支持findAndModify命令。有关详细信息,请参见下面的答案。