我是刚学习nodejs不久的菜鸟,最近学习的时候遇到个问题,想问一下: 我一个项目里面,项目里有奖品,奖品有1个。当为0个时就领取失败 前端有个按钮可以点击领取奖品,此时同时有2个人点击领取。我在后台的写法就是,先查询奖品个数,也就是 先conn.query(“select * from giftinfo” , function(row){ }) 获取到奖品个数,然后就在回调中进行计算,把奖品个数减一再更新上去。也就是在回调里再进行conn.query(“update …” , function(){})
我自己的项目里在回调中还有几个数据库更新操作,现在的情况就是,同时点击的话,有时会出现奖品只有1个,但是却2个人都领到了奖品。 我自己想了一下,是不是当同时点击时,首先一个人先触发conn.query方法查询,此时查询结果还没返还的时候,nodejs又去执行第二个人的query,于是在两个的查询回调当中,奖品的数量都是一致的,所以都是可以领取。
请问这种情况一般是如何处理的呢?
想起之前看的课堂,将的是如何处理12306抢票的问题。
貌似用的解决办法是锁什么的。
var events= require(‘events’); var x = new events.EventEmitter(); var jpCount = 5; //conn.query(“select * from giftinfo” , function(row){ })
x.on(‘lingjiang’,lingjiangHandle); function lingjiang(user) { if (jpCount > 0) x.emit(‘lingjiang’, user); else { console.log(‘奖品已领玩’); } }
var lingjiangHandle = function(user) { jpCount–; if (lingjiangSucceed) { console.log(user + ‘领奖成功’); } else { console.log(user + ‘领奖失败’); jpCount++; } }
mongo没有锁的概念,这时候可以考虑使用redis
这么做是不正确的。
很明显,你对数据库做了写操作,单单查询来判断是不够的。必须修改数据库成功,才能为用户颁发奖品,如果失败,则意味奖品不存在。
还有一种就是同步缓存,在nodejs服务器记录一个奖品数作为查询和递减,数据库服务器只负责修改。但是nodejs服务器挂掉会导致这个缓存消失,每次重新启动nodejs服务器,需要先去数据库服务器确认当前可以领的奖品数。
mongodb是存在写锁的。
@sandheart 谢谢,我知道该如何做了
@tulayang 好的,我知道该如何做了,感谢教导
用缓存。缓存对于事件I/O几乎0延迟。
看着这个issue之后, 解决掉了机器人并发刷邮件的严重问题. 多谢4楼.
楼上各位说得真好
可以用一个锁,点击后,只有抢到锁的请求可以继续执行数据库操作。 具体锁怎么实现就多种多样了:
- 可以使用一个互斥队列,rabbitmq。
- 可以利用mongodb更新单个数据是原子操作这一特性,建一张表,里面放一条数据,有一个列为locked,默认是false,每次点击首先去执行更新这条数据的操作,将locked=true,更新成功的即为抢到了锁。
@yunheli mongodb有锁。