mongodb怎么处理这种需求
发布于 6 年前 作者 lovegnep 4051 次浏览 来自 问答

假设如下场景:

玩家A与B拥有同一份背包,背包中包括玩家道具,是否已经种花等信息,如果没有种花,则两个玩家都可以进行种花,但是一旦其中一个玩家种花了,另一个就不能种了。种花的时候会扣除相应的道具。


背包文档如下:

	{
	itemList:[
		{id:20,num:12},
		{id:1,num:10},
		{id:2,num:12},
		{id:12,num:12}],
	flowerId:0
	}
  • flowerId如果为0表示玩家A与B都可以种花
  • 种花的操作可以简单表示为将flowerId置1
  • 种花操作需要扣除id为1的道具2个,id为2的道具3个
  • A与B随时都可以进行其它操作导致道具数量变化
  • 如果道具数量不足则不能种花。

因为在分布式系统中A与B可以同时进行种花的操作,如果处理不好会导致道具被扣除两次或者道具不够依然种花成功,那要怎么保证数据的一致性呢?

解决办法,目前只想到这2个:

  1. 对背包文档进行加锁,这种操作感觉不太优雅,有可能存在死锁的情况。
  2. 由于mongodb对同一个文档的操作是原子性的,所以可以考虑用一条update命令来实现判断是否种花,判断道具数量足够,flowerId置1,减少相关道具数量,但还没有找到一条命令就能实现这些操作的命令。
23 回复

findandmodify不就解决了

来自✨ Node.js开源项目精选

@vendar findandmodify这一条命令不能同时实现判断是否种花,判断道具数量足够,flowerId置1,减少相关道具数量,

@lovegnep 可以说下你怎么用这条命令吗?这一条命令不够吧?

给你个思路,自己试试看。查找条件是,flowerid=0 并且 element match 道具 数量 >= 需要数量。update内容就是flowerid置1,increase相关element属性值。具体参考下mongodb官方文档。另外,mongodb不需要加锁,一个collection上的原子操作可以通过bulk达成。

来自✨ Node.js开源项目精选

@vendar 谢谢指导。

下面这句怎么写呢,我查了好多文档也没找到对数组内嵌文档指定元素进行更新的操作

increase相关element属性值

来自酷炫的 CNodeMD

@vendar 这个我之前已经浏览过,通过$ 或者 $[<identifier>]操作符也只能一次针对数组中的一个元素进行修改或者用同一个更新条件针对数组中的多个元素(由于这里的更新条件是多个,所以此处不成立),也就是说要想实现扣除id为1的道具2个,id为2的道具3个就需要两步来实现。 所以最后还是要用到bulk包一下吗?

arrayFilters支持数组,用两个条件分别匹配应该可以做到的,注意需要mongodb3.6+。再不行就用bulk吧。

来自✨ Node.js开源项目精选

@vendar 敲了如下命令后

  db.getCollection('test').update({flowerId:0},
  {$inc:{"itemList.$[elem1].num":-2},$inc:{"itemList.$[elem2].num":-3}},
  {arrayFilters:[{"elem1.id":1},{"elem2.id":2}]}
  )

输出如下:

No array filter found for identifier 'elem2' in path 'itemList.$[elem2].num'

应该是不能这样用,看了文档,貌似是同时满足arrayFilters中的条件的元素再执行更新操作 不能实现针对特定元素进行特定更新


db.[表].updateOne(
	{
		flowerId:0,
		itemList.1.num:{
			$gt: 2
		},
		itemList.2.num: {
			$gt: 3
		}
	},
	{
		// ....
	}
)

我是小菜鸟。。。。。。。。。不知道对不对

大佬,Node带带我这个小前端

@BengBu-YueZhang 道具位置不固定的,不能用这种

嘻嘻。我有好办法了


db.[表].updateOne(
	{
		flowerId:0,
		// 字段的顺序,需要和表里顺序一致
		itemList: {
			id:2,
			num: {
				$gt: 3
			}
		}
	},
	{
		// ....
	}
)

不知道上面的那种,支不支持数组的写法

@lovegnep 如果是这样好像只能bulk了

来自✨ Node.js开源项目精选

@vendar 后来在stackoverflow上提问,有人提供了另外一种方法,和我们的基本一样,但是成功了。

db.test.update(
{ flowerId:0 },
{ $inc: { "itemList.$[elem1].num": -2, "itemList.$[elem2].num": -3} },
{ arrayFilters: [ {"elem1.id":1}, {"elem2.id":2} ] }
)

地址如下

https://stackoverflow.com/questions/50979490/how-to-update-element-in-array-using-different-update-document-for-different-arr

cool~ 思路是一样的,只是刚开始inc语法写错了,我也没注意到

来自✨ Node.js开源项目精选

mongo的文档,要好好看看了,之前只看了mongo的crud的部分,就去看mongoose了,

@vendar 看来以后要多去栈溢出逛逛

来自酷炫的 CNodeMD

回到顶部