【问题标题】:Node.js + MySQL - handling transactionsNode.js + MySQL - 处理事务
【发布时间】:2014-02-19 23:37:52
【问题描述】:

我正在使用 express 和 node-mysql 驱动程序在 node.js 上构建应用程序。当我需要进行一系列数据库插入/更新时,我的应用程序中有几种情况。我希望它们在一个事务中,如果第二个或第三个失败,以前的插入完全回滚。

目前,我这样做的方式是让某种中间件在请求到达时执行START TRANSACTION。在处理请求的过程中,如果抛出任何错误,我会捕获此错误,并执行ROLLBACK。如果没有发生错误,我会在将响应发送到浏览器之前执行COMMIT

但是,我现在担心当多个用户同时访问应用程序时这将不起作用,因为如果另一个请求尝试使用 START TRANSACTION 开始它自己的事务,MySQL 会强制提交!我目前只使用一个节点实例,并为所有请求使用一个 MySQL 连接。

如果我的担忧是正确的,有人可以给我建议吗?我应该如何获得交易支持?

【问题讨论】:

  • 如果您可以选择使用 couch / redis / mongo 代替,因为它们更容易与 node 一起使用。如果您已经有一个 MYSQL 数据库,请继续使用它。
  • @Raynos AFAIK MongoDb 不支持事务...

标签: javascript mysql database transactions node.js


【解决方案1】:

查看https://github.com/bminer/node-mysql-queues

我为 node-mysql 实现了一个小包装器来支持事务和多个语句。它还没有经过测试,也没有准备好生产......但它会在几天内完成。 :)

更新:我现在已经对这个库进行了相当彻底的测试......应该很好!

【讨论】:

    【解决方案2】:

    根据您的事务的复杂程度,您可能会在尝试将来自 Node 的查询排队时遇到一些丑陋的嵌套,这可能会引入丑陋的变量范围问题。

    您可以做的是编写一个存储过程并通过SELECT添加成功/失败标志来结束它,然后使用node-mysql 查询该过程,就像使用SELECT 查询一样。以下是存储过程的外观:

    DELIMITER //
    DROP PROCEDURE IF EXISTS MyProcedure //
    CREATE PROCEDURE MyProcedure(IN param1 VARCHAR/*, My, Parameters, ... */)
    BEGIN
    
        DECLARE EXIT HANDLER FOR NOT FOUND, SQLWARNING, SQLEXCEPTION
        BEGIN
            ROLLBACK;
            SELECT 0 AS res;
        END;
    
        START TRANSACTION;
        # My Transaction ...
    
    
        COMMIT;
        SELECT 1 AS res;
    
    END //
    DELIMITER ;
    

    您的节点代码如下所示:

    var mysql = require('mysql');
    
    var client = mysql.createClient({
        host    : '127.0.0.1',
        user    : 'username',
        password: 'password'
    });
    client.query('USE mydatabase');
    
    var myParams = "'param1', 'param2', ... ";
    client.query("CALL MyProcedure(" + myParams + ")", function(err, results, fields) {
        if (err || results[0].res === 0) {
            throw new Error("My Error ... ");
        } else {
            // My Callback Stuff ...
    
        }
    });
    

    【讨论】:

    • 这实际上是解决问题的一种非常有趣的方法——我在想,也许在服务器启动时预处理查询并将对 ORM 的调用转换为存储过程?我很想听听你对最好的工作方式的看法,如果你给我发个便条的话。我正在开发一个名为 Waterline 的跨数据库 ORM。
    【解决方案3】:

    您需要创建一个客户端池,或者以其他方式确保两个不同的页面不会在同一连接上散布命令(至少在它们中的任何一个处于事务中时)。

    由于您希望根据先前命令的结果有条件地进行回滚,因此您需要通过它们的回调将 db 调用链接在一起,而不是依赖于 node-mysql 的排队行为。这将为其他页面打开一个窗口,并按照您的建议在同一连接上排队执行操作。

    您可以创建和管理自己的队列,但这最终会序列化所有事务页面(假设您坚持使用单连接模型)。

    通过快速谷歌搜索,看起来 github 上有几个 node-mysql 池。但是,在查看它们之后,它们看起来并不能帮助您解决问题。

    【讨论】:

      【解决方案4】:

      我很难相信如果一个单独的会话执行START TRANSACTION 其他事务已提交。这将是完全不安全的,尤其是当数据需要回滚(或者它是否“回滚”?)时。

      您是否有可能将其与 同一会话 START TRANSACTION 混为一谈?
      请参阅http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html,其中解释了事务不能嵌套。这当然适用于同一会话,而不适用于其他用户的会话。

      假设您没有弄乱会话的隔离级别或全局隔离级别,那么事务应该是安全的。

      无论如何,如果您想对事务进行排队,那么在节点中构建一个全局队列对象并将调用链接起来并不难(因此一个在另一个完成时开始)。一个带有 push 和 pop 的简单数组应该可以解决问题。

      【讨论】:

      • 在 MySQL 的上下文中,一个连接等于一个“会话”。 OP 使用单个连接,因此它是一个跨所有请求处理共享的 MySQL 会话。
      • @SystemParadox,这是真的。我理解这个问题是单独连接的问题,当 OP 说“单一连接”时,他/她的意思是每个用户。
      【解决方案5】:

      只是一个想法:在 postresql 上,您可以启动一个事务并为其设置一个 ID。那么,您可以重用相同的连接,因为如果您需要提交或回滚,您将通过 id 引用您的事务,对吧?

      【讨论】:

        猜你喜欢
        • 2016-05-04
        • 2021-07-13
        • 2013-03-23
        • 1970-01-01
        • 2013-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多