Javascript 有人破坏了我的提问

Javascript 有人破坏了我的提问,javascript,mysql,node.js,Javascript,Mysql,Node.js,我一直在尝试使用查询进行交易;我已经这样做了,首先我检查并执行一个查询来检查用户是否有足够的余额,然后我扣除他的余额并处理事务 问题是,如果你下载一个工具,或者使用一个每秒可以点击200次的宏(我认为),信号发送的速度比查询处理的速度快,因此它仍然会认为用户有足够的余额,而他最终没有,他的余额将变为负值 这是快速代码 var processTransaction = function(userid, cost){ database.query('SELECT `balance` FROM

我一直在尝试使用查询进行交易;我已经这样做了,首先我检查并执行一个查询来检查用户是否有足够的余额,然后我扣除他的余额并处理事务

问题是,如果你下载一个工具,或者使用一个每秒可以点击200次的宏(我认为),信号发送的速度比查询处理的速度快,因此它仍然会认为用户有足够的余额,而他最终没有,他的余额将变为负值

这是快速代码

var processTransaction = function(userid, cost){
    database.query('SELECT `balance` FROM `user` WHERE `id` = ' + database.pool.escape(userid), function(err, row){
        if(err){
            return;
        }

        if(!row.length){
            return;
        }

        var userBalance = row[0].balance;
        if(userBalance >= cost){
            /* User has enough, process */

            addBalance(userid, -cost); //deduct query
        }
    });
}
我在这里犯了什么错误吗?我是否有不同的做法

查询功能

var query = function(sql, callback) {
  if (typeof callback === 'undefined') {
    callback = function() {};
  }
  pool.getConnection(function(err, connection) {
    if(err) return callback(err);
    connection.query(sql, function(err, rows) {
      if(err) return callback(err);
      connection.release();
      return callback(null, rows);
    });
  });
};

您需要确保数据库处于一致状态。您可以通过几种方式实现这一点,其中最简单的方式是:

  • 使用
    LOCK TABLE user WRITE
    阻止访问用户表(并在完成后立即解锁表)。如果你点击200次,不用担心——点击将全部排队,无法同时运行
本文件来自:

使用锁定表和解锁表的正确方法 事务表(如InnoDB表)是启动事务的基础 设置自动提交=0(不启动事务)后加锁 表,并且在提交事务之前不调用解锁表 明确地例如,如果需要写入表t1并读取 从表t2中,可以执行以下操作:

SET autocommit=0;
LOCK TABLES t1 WRITE, t2 READ, ...;
... do something with tables t1 and t2 here ...
COMMIT;
UNLOCK TABLES;
当您调用锁表时,InnoDB在内部使用自己的表锁, MySQL拥有自己的表锁。InnoDB发布其内部表 在下一次提交时锁定,但是为了让MySQL释放其表锁,您需要 必须调用解锁表。您不应该将自动提交设置为1, 因为InnoDB会在之后立即释放其内部表锁 调用锁表和死锁很容易发生。InnoDB 如果autocommit=1,则根本不获取内部表锁,以 帮助旧应用程序避免不必要的死锁


您需要确保数据库处于一致状态。您可以通过几种方式实现这一点,其中最简单的方式是:

  • 使用
    LOCK TABLE user WRITE
    阻止访问用户表(并在完成后立即解锁表)。如果你点击200次,不用担心——点击将全部排队,无法同时运行
本文件来自:

使用锁定表和解锁表的正确方法 事务表(如InnoDB表)是启动事务的基础 设置自动提交=0(不启动事务)后加锁 表,并且在提交事务之前不调用解锁表 明确地例如,如果需要写入表t1并读取 从表t2中,可以执行以下操作:

SET autocommit=0;
LOCK TABLES t1 WRITE, t2 READ, ...;
... do something with tables t1 and t2 here ...
COMMIT;
UNLOCK TABLES;
当您调用锁表时,InnoDB在内部使用自己的表锁, MySQL拥有自己的表锁。InnoDB发布其内部表 在下一次提交时锁定,但是为了让MySQL释放其表锁,您需要 必须调用解锁表。您不应该将自动提交设置为1, 因为InnoDB会在之后立即释放其内部表锁 调用锁表和死锁很容易发生。InnoDB 如果autocommit=1,则根本不获取内部表锁,以 帮助旧应用程序避免不必要的死锁


利用“排队”体系结构还可以让您更容易地处理并发请求。其概念是将所有请求推送到一个队列中。有一个worker设置为“轮询”这个队列,可能是在某种cron上,从队列中读取数据并分配工作。当worker收到项目时,它将更按顺序发出更新/创建请求,并防止出现争用情况。对于这种类型的行为,您将需要更好地处理异步事件。

利用“排队”体系结构还可以让您更容易地处理并发请求。其概念是将所有请求推送到一个队列中。有一个worker设置为“轮询”这个队列,可能是在某种cron上,从队列中读取数据并分配工作。当worker收到项目时,它将更按顺序发出更新/创建请求,并防止出现争用情况。使用这种类型的行为,您需要有更多的能力来处理异步事件。

在不选择
的情况下进行
UPDATE
怎么样

UPDATE user_balance 
SET balance = balance - :value 
WHERE user_id = :user_id AND balance >= :value
我相信这种方法和交易本身应该有助于避免达到负平衡


[UPD]为了检测余额是否已实际更新(因为确实存在无效余额),您可以读取受影响的行数(取决于所使用的库,它可以是分离的方法或查询)

如果不选择
而进行
更新
如何

UPDATE user_balance 
SET balance = balance - :value 
WHERE user_id = :user_id AND balance >= :value
我相信这种方法和交易本身应该有助于避免达到负平衡


[UPD]并且为了检测余额是否已实际更新(因为确实存在无效余额),您可以读取受影响的行数(取决于使用的库,它可以是分离的方法或查询)

在mysql中,您可以使用
选择。。。用于更新
以锁定row@IłyaBursov‘lock row’?是的,锁定row以防止在另一个线程中进行更新您也可以尝试将所有内容放入一个查询中,例如
update user SET balance=balance-x,其中id=y和balance>=x
@riv I当前有许多节以相同的方式使用查询,很遗憾,我需要很长时间才能将所有内容更改为适合一个查询。在mysql中,您可以使用
select。。。用于更新
以锁定row@IłyaBursov‘lock row’?是的,锁定row以防止在另一个线程中进行更新您也可以尝试将所有内容放入一个查询中,例如
update user SET balance=balance-x,其中id=y和balance>=x
@riv I当前有许多节以相同的方式使用查询,我要花很长时间才能改变一切