Node.js SQL竞争条件

时间:2017-06-30 08:04:57

标签: sql node.js race-condition

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

2 个答案:

答案 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模块中。

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