MySQL事务没有停止for循环的竞争条件

Posted

技术标签:

【中文标题】MySQL事务没有停止for循环的竞争条件【英文标题】:MySQL transactions not stopping race conditions from for loop 【发布时间】: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?你打算在这两者之间做点什么吗? 据我所知防止竞争状况 【参考方案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 命令(两次都有效),另一次尝试(等待第一个)。

【讨论】:

如果不是那么明显,如果命令通过同一连接发送,则不存在竞争条件。 如果你使用连接池怎么办? 我认为连接池应该可以很好地处理它。通常每个连接一次只被一个进程使用。

以上是关于MySQL事务没有停止for循环的竞争条件的主要内容,如果未能解决你的问题,请参考以下文章

如何强制结束 for 循环的迭代(不停止 for 循环)?

使用数据库事务防止竞争条件 (Laravel)

Python里for和while的区别(74)

Python break 语句

阿里一面 | 说说你对 MySQL 死锁的理解

python语言如何结尾?