使用mysql和node.js的可序列化事务
我在使用node.js和mysql进行事务时遇到问题。 问题是我的事务不会孤立运行,即使我将隔离级别设置为“可序列化”。
我设置了以下最小的例子来说明我的问题。 我正在使用具有两个columns(id,val)的单个表:
CREATE TABLE `A` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `val` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO `A` (`id`, `val`) VALUES (1,0);
请注意,我正在解除InnoDB表types,因为它支持事务。
我的node.js程序只是读取表A中单行的值,并使用update语句来增加它。 select语句使用FOR UPDATE修饰符来获得该行的locking。 这两个sql语句被封装到一个事务中,并且我在for循环中执行了这些事务中的10个:
var orm = require("orm"); orm.connect("mysql://root@localhost/test", function (err, db) { db.driver.execQuery("SET GLOBAL tx_isolation='SERIALIZABLE';", function (err, data) { for (var i = 0; i < 10; i++) { db.driver.execQuery("START TRANSACTION;", function (err, data) { db.driver.execQuery("SELECT * FROM A FOR UPDATE;", function (err, data) { var value = data[0].val console.log('reading value: ',value) db.driver.execQuery("UPDATE A SET val="+value+"+1 WHERE id=1", function (err, data) { console.log('writing value: ', value+1) db.driver.execQuery("COMMIT;", function (err, data) {}) }) }) }) } }) })
我希望在运行这个代码之后,存储在表A中的值会增加10,但是它只会增加1。
为了理解发生了什么,我在代码中添加了打印输出。 我希望看到打印输出
reading value 0 writing value 1 reading value 1 writing value 2 ...
但是我得到打印输出
reading value 0 reading value 0 ... writing value 1 writing value 1 ...
解决这个问题的一种方法是为每个事务build立一个新的数据库连接,但是由于性能方面的原因,我不希望这样做。
有人可以解释发生了什么,以及我可以如何将上述变成node.js中事务的工作示例?
好的,我们find了一个使用node-mysql-transaction软件包的解决scheme(见下面的代码)。
var mysql = require('mysql'); var transaction = require('node-mysql-transaction'); var trCon = transaction({ connection: [mysql.createConnection,{ host : 'localhost', database : 'test', user : 'root', password : 'root', }], dynamicConnection: 32, idleConnectionCutoffTime: 1000, timeout:600 }); for(var i=0;i<10;i++) { trCon.set(function(err, safeCon){ safeCon.query('SELECT * FROM A FOR UPDATE;',[],function(err,result){ if (err) safeCon.rollback() var val = result[0].val val += 1 safeCon.query('UPDATE A SET val='+val,[],function(err,result){ if (err) safeCon.rollback(err) else safeCon.commit(); }) }) }) }