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循环的竞争条件的主要内容,如果未能解决你的问题,请参考以下文章