使用 mysql 和 node.js 的可序列化事务

Posted

技术标签:

【中文标题】使用 mysql 和 node.js 的可序列化事务【英文标题】:Serializable transactions using mysql and node.js 【发布时间】:2014-11-22 21:56:29 【问题描述】:

我在使用 node.js 和 mysql 的事务方面遇到问题。问题是我的事务不是独立运行的,即使我将隔离级别设置为“可序列化”。

我设置了以下最小示例来举例说明我的问题。我正在使用带有两个列(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 表类型,因为它支持事务。

我的 node.js 程序只是从表 A 中的单行读取值,并使用更新语句递增它。 select 语句使用 FOR UPDATE 修饰符来获得对行的锁定。两条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
...

解决问题的一种方法是为每个事务建立一个新的数据库连接,但出于性能原因,我不希望这样做。

有人可以解释发生了什么,以及如何将上述内容更改为 node.js 中事务的工作示例吗?

【问题讨论】:

【参考方案1】:

好的,我们找到了使用node-mysql-transaction 包的解决方案(参见下面的代码)。

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();
    )
  )
)    

【讨论】:

以上是关于使用 mysql 和 node.js 的可序列化事务的主要内容,如果未能解决你的问题,请参考以下文章

node.js 同步 mysql 查询

使用 node.js 更正了大型 RESTful API 的可扩展结构

前后端分离与Node和NPM的那些事

入门 node.js 你必须知道的那些事

Node.js 将相同的可读流输送到多个(可写)目标中

关于 node.js 和 mysql