【问题标题】:MySQL transactions not stopping race conditions from for loopMySQL事务没有停止for循环的竞争条件
【发布时间】:2013-10-17 12:11:55
【问题描述】:

我在我的应用程序中使用node-mysql。我尝试实现SELECT FOR UPDATE,但我无法让它正常工作。我遇到的问题类似于下面的代码。下面for循环中的第一个事务没有成功阻塞第二个事务。相反,两个事务同时 SELECT FOR UPDATE 并获得相同的记录。我怎样才能解决这个问题?谢谢!

var testTransaction = function (count) {
  connection.beginTransaction(function(err) {
    if (err) throw err;
    db.query('SELECT * FROM myTable WHERE id = 1 FOR UPDATE', function(err, rows, result) {
      if (err) { 
        connection.rollback(function() {
          throw err;
        });
      }
      connection.query('UPDATE myTable SET myField=? WHERE id=1', (count + 1), function(err, result) {
        if (err) { 
          db.rollback(function() {
            throw err;
          });
        }  
        connection.commit(function(err) {
          if (err) { 
            db.rollback(function() {
              throw err;
            });
          }
          console.log('success!');
        });
      });

    }); 

  });
}

for (var i = 0; i < 2; i++) {
  testTransaction(i);
}

【问题讨论】:

  • 您使用的是哪个存储引擎?
  • 我问,因为我听说只有 InnoDB 支持一致性读取,我不知道这是否会有所作为。
  • 我真的不明白为什么您需要在进行更新之前执行SELECT FOR UPDATE?你打算在这两者之间做点什么吗?
  • 据我所知防止竞争状况

标签: mysql sql node.js node-mysql


【解决方案1】:

node-mysql 模块中的方法是异步编写的,因此不会阻塞应用程序。如果你没有理由使用循环,那么你可以在你的函数中添加一个回调并嵌套执行:

var testTransaction = function(count, callback) {
  connection.beginTransaction(function (err) {
    if (err) throw err;
    db.query('SELECT * FROM myTable WHERE id = 1 FOR UPDATE', function (err, rows, result) {
      if (err) {
        connection.rollback(function() {
          return callback(err);
        });
      }
      connection.query('UPDATE myTable SET myField=? WHERE id=1', (count + 1), function (err, result) {
        if (err) {
          db.rollback(function() {
            return callback(err);
          });
        }
        connection.commit(function (err) {
          if (err) {
            db.rollback(function() {
              return callback(err);
            });
          }
          callback(null);
          console.log('success!');
        });
      });
    });
  });
};

testTransaction(0, function(err) {
  testTransaction(1, function(err) {
    // both operations have completed
  });
});

如果您出于某种原因需要循环异步函数,那么我会看看 async 库。

【讨论】:

  • 我认为问题在于询问者希望交易“阻止”读/写 - 这意味着一对 SELECT/UPDATE 将在下一个交易开始之前完成。
【解决方案2】:

你的问题是你使用的是同一个连接。

试试这样的:

var mysql      = require('mysql');
function conn() {
    var connection = mysql.createConnection({
        host     : 'localhost',
        user     : 'user',
        password : 'pass',
        database : 'test'
    });
    return connection;
}

var testTransaction = function (connection,count) {
    connection.beginTransaction(function(err) {
        if (err) throw err;
        connection.query('SELECT * FROM table WHERE id = 1 FOR UPDATE', function(err, rows, result) {
            console.log(rows);
            if (err) {
                connection.rollback(function() {
                    throw err;
                });
            }
            connection.query('UPDATE table SET name=? WHERE id=1', (count + 1), function(err, result) {
                if (err) {
                    connection.rollback(function() {
                        throw err;
                    });
                }
                setTimeout(function(){
                    connection.commit(function(err) {
                        if (err) {
                            connection.rollback(function() {
                                throw err;
                            });
                        }
                        console.log('success!');
                    });
                },2000);
            });

        });

    });
}

for (var i = 0; i < 2; i++) {
    testTransaction(conn(),i);
}

请注意,它为每个事务使用不同的连接。

在命令行 mysql 中,您可以复制类似的内容,方法是打开两个连接,在两者中发出 start transaction,然后一次尝试两次 select ... for update 命令(两次都有效),另一次尝试(等待第一个)。

【讨论】:

  • 如果不是那么明显,如果命令通过同一连接发送,则不存在竞争条件。
  • 如果你使用连接池怎么办?
  • 我认为连接池应该可以很好地处理它。通常每个连接一次只被一个进程使用。
猜你喜欢
  • 1970-01-01
  • 2017-03-24
  • 2018-07-24
  • 1970-01-01
  • 2016-03-21
  • 2020-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多