Node.js SQL争用条件

我想知道在执行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]); } 

假设数据库节点连接到负载较重,并且需要一些时间来完成每个请求,并且每次用户点击某个项目上的购买button时都会调用该函数。

当用户开始发送这个button或者让机器人发送购买请求时会发生什么? 在我的理解,第一个请求将执行SELECT查询,并恢复执行。 现在,由于用户正在垃圾邮件button,下一个请求进来,执行相同的function。 现在你有几个SELECT语句正在等待完成。 现在,几个select语句完成执行并执行callback,因此现在有几个callback函数正在执行UPDATE语句,所有语句都具有相同的余额。 这意味着,如果起始余额为5,那么所有这些数额都会从最后一个已知余额中减less。 所以基本上不pipe你同时执行这个查询的频率如何,第一批请求都将从SELECT查询中获得相同的值,并将其更新为一些假的。

对不起,如果我的解释有点模糊,但我认为这是一个非常现实的问题,我怀疑有很多人考虑。

所以要解决这个问题,Node是否支持类似互斥的东西? 从我读过的MongoDB不支持表锁,所以locking只能是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; } } 

你可以简单的做

 sql.query('UPDATE user SET balance = balance - ? WHERE userid = ?', [amount, userId]); 

这里没有竞争条件,也没有使用交易。

如果它是一种支付types的情况下,你必须更新和插入许多文件,你的服务器将在几分钟内得到很多命中,或者你可以使用var q = async.queue(function (task, callback) { function(){} }, 5); 或者将整个函数放入一个一个一个地执行的async.waterfall模块中。

https://caolan.github.io/async/docs.html#waterfall