我想知道在执行IO时如何防止NodeJS中的竞争条件。我一直在阅读,并且每个人都坚持认为种族条件是不可能的,因为NodeJS是单线程的。
让我们看看下面的伪代码:
async decreaseUserBalance(userId, amount) {
const currentBalance = await sql.queryScalar('SELECT balance FROM user WHERE userid=?', [userId]);
await sql.query('UPDATE user SET balance = ? WHERE userid = ?', [currentBalance - amount, userId]);
}
让我们说连接的数据库节点负载很重,并且需要一些时间来完成每个请求,并且每次用户点击某个项目上的购买按钮时都会调用此函数。
当用户开始发送垃圾邮件或让机器人发送购买请求时会发生什么?根据我的理解,第一个请求将执行SELECT查询,并恢复执行。现在,由于用户正在向按钮发送垃圾邮件,因此下一个请求进入,执行相同的功能。现在您有几个SELECT语句等待完成。现在,一些select语句完成执行并执行回调,因此现在有几个回调正在执行UPDATE语句,所有这些都具有相同的余额。这意味着如果起始余额为5,则所有余额将从最后的已知余额减少var 金额。所以基本上你同时执行这个查询的频率并不重要,第一批请求将从SELECT查询获得相同的值,并将其更新为一些伪造。
很抱歉,如果我的解释有点模糊,但我相信这是一个非常现实的问题,我怀疑很多人都会考虑。
所以要解决这个问题,Node是否支持互斥量?根据我的阅读,MongoDB不支持表锁定,因此锁定只是SQL的一个选项。
编辑:
我知道我可以在1个查询中完成这个例子,那会解决它,但是我们说这是不可能的。那你怎么解决这个问题呢?
编辑2:
好的,让我们来看看这个例子:
async tryBuyItem(userId, itemId, price) {
//Do we still have enough balance?
const currentBalance = await sql.queryScalar('SELECT balance FROM user WHERE userid=?', [userId]);
if (currentBalance >= price) {
await sql.query('UPDATE user SET balance = balance - ? WHERE userid = ?', [price, userId]);
await sql.query('INSERT INTO user_items (userid, itemid) VALUES (?, ?)', [userId, itemId]);
return true;
} else {
return false;
}
}
答案 0 :(得分:1)
你可以简单地做
sql.query('UPDATE user SET balance = balance - ? WHERE userid = ?', [amount, userId]);
这里没有竞争条件,也没有使用过任何交易。
答案 1 :(得分:1)
如果它是一种支付类型的情况,你必须更新并插入许多文件,你的服务器将在几分钟内获得很多点击,要么你可以使用
var q = async.queue(function (task, callback) {
function(){}
}, 5);
处理或将整个函数放入一个逐步执行的async.waterfall模块中。