Node.js + MySQL - 处理事务

发布于 2024-11-05 16:11:46 字数 464 浏览 1 评论 0 原文

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

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

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

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

I am building an app on node.js using express, and node-mysql driver. There is a couple of cases in my app when I need to make a series of database inserts/updates. I want them in a transaction such that if the second or third one fails, the previous inserts are rolled back completely.

Currently, the way I am doing this is to have some kind of middleware which does a START TRANSACTION when a request arrives. During the course of processing of the request, if any error is thrown, I catch this error, and do a ROLLBACK. If no error occurs, I do a COMMIT before sending the response to the browser.

However, I am now concerned that this won't work when multiple users access the application simultaneously, as MySQL does a forced commit if another request tries to begin it's own transaction with START TRANSACTION! I am currently using only a single instance of node, and a single MySQL connection for all the requests.

Can someone please advice me if my concerns are valid, and how should I get in transactions support?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

无法回应 2024-11-12 16:11:47

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

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

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

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

I find it hard to believe that if a separate session executes a START TRANSACTION that other transactions are committed. That would be totally unsafe, especially when data needs to be rollbacked (or is it "rolled back"?).

Is it possible you're mixing this up with a same session START TRANSACTION?
See http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html where it explains that transactions cannot be nested. That of course applies to the same session, not to another user's session.

Assuming you haven't messed around with the isolation level of your session, or the global isolation level, then transactions should be safe.

In any case, if you wanted to queue your transactions it wouldn't be hard to build a global queue object in node and chain the calls (so one starts when another finishes). A simple array with push and pop should do the trick.

别低头,皇冠会掉 2024-11-12 16:11:47

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

Just an idea: on postresql you can start a transaction and set an ID to it. So then, you could be reusing the same connection around, because in case you need to commit or rollback, you are going to refer to your transaction by id, right?

客…行舟 2024-11-12 16:11:46

查看

我实现的 https://github.com/bminer/node-mysql-queues Node-mysql 的一个小包装器,用于支持事务和多个语句。它尚未经过测试,也尚未准备好投入生产……但几天后就会准备好。 :)

更新:我现在已经非常彻底地测试了这个库......应该很好!

Check out https://github.com/bminer/node-mysql-queues

I implemented a little wrapper for node-mysql to support transactions and multiple statements. It has not been tested, and is NOT production ready... but it will be in a few days. :)

UPDATE: I have tested this library pretty thoroughly now... should be good to go!

咆哮 2024-11-12 16:11:46

根据事务的复杂程度,您可能会遇到一些丑陋的嵌套,试图对来自 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 ...

    }
});

Depending on how complex your transaction is you might run into some ugly nesting trying to queue your queries from Node, which might introduce ugly variable scoping issues.

What you can do instead is write a stored procedure and end it by SELECTing a success/failure flag, then query the procedure with node-mysql as you would a SELECT query. Here's how the stored procedure might look:

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 ;

Your Node code would look something like this:

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 ...

    }
});
夏末 2024-11-12 16:11:46

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

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

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

从快速谷歌搜索来看,github 上似乎有几个 node-mysql 池。然而,在查看它们之后,它们看起来并不能帮助解决您的问题。

You'll need to create a client pool, or somehow otherwise ensure that two different pages aren't interspersing commands on the same connection (at least while any of them is in a transaction).

Since you want to conditionally do a rollback based upon the result of an earlier command, you'll need to chain the db calls together through their callbacks and not rely on the node-mysql queuing behavior. That will open up a window for some other page to come in and queue up an operation on the same connection as you suggest.

You could create and manage your own queue, but that would end up serializing all transactional pages (assuming you're sticking with the single connection model).

From a quick googling, it looks like there are several node-mysql pools on github. After looking at them, though, they don't look like they'll help with your issue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文