在 Node.js 中处理回滚的 MySQL 事务

Posted

技术标签:

【中文标题】在 Node.js 中处理回滚的 MySQL 事务【英文标题】:Handling rollbacked MySQL transactions in Node.js 【发布时间】:2016-05-04 05:55:01 【问题描述】:

我这几天一直在处理一个问题,我真的希望你能帮助我。

这是一个基于 node.js 的 API,使用 sequelize 表示 mysql

在某些 API 调用中,代码会启动锁定某些表的 SQL 事务,如果我同时向 API 发送多个请求,则会收到 LOCK_WAIT_TIMEOUT 错误。

var SQLProcess = function () 
    var self = this;
    var _arguments = arguments;

    return sequelize.transaction(function (transaction) 
            return doSomething(transaction: transactioin);
        )
        .catch(function (error) 
            if (error && error.original && error.original.code === 'ER_LOCK_WAIT_TIMEOUT') 

                return Promise.delay(Math.random() * 1000)
                    .then(function () 
                        return SQLProcess.apply(self, _arguments);
                    );

             else 
                throw error;
            
        );
;

我的问题是,同时运行的请求相互锁定了很长时间,我的请求在很长一段时间(~60秒)后返回。

我希望我能解释清楚易懂,你可以给我一些解决方案。

【问题讨论】:

这不是node.jssequelize 麻烦,这是来自MySQL 1205 Lock wait timeout exceeded; try restarting transaction 的错误消息。您应该查看 MySQL 服务器中的事务及其状态。 非常清楚,为什么我会收到此错误。我正在寻找如何处理它的解决方案。 只有一种方法可以了解发生了什么,那就是SHOW ENGINE INNODB STATUS\G。根据我的经验,我在 MySQL 中多次遇到死锁事务。 目前还不清楚您期望得到什么样的答案。 “把手”是什么意思?你想摆脱锁定(在这种情况下,你的doSomething 做什么很重要)?或者你想重复你的交易(在这种情况下,你为什么不从你的处理程序再次运行doSomething?或者你的catch语句不起作用,你想让它起作用?我有一个很大的列表与死锁有关的随机笔记here,现在我看起来很困惑,但是检查一下,也许你会发现一些有用的东西。 【参考方案1】:

死锁的主要原因是糟糕的数据库设计。如果没有有关您的数据库设计的更多信息以及哪些确切的查询可能会或可能不会相互锁定,就不可能为您的问题提供特定的解决方案。 不过,我可以为您提供解决此问题的一般建议/方法:

我会确保您的数据库至少在 第三范式 中是normalized,或者,如果这还不够进一步的话。可能有一些工具可以为您自动执行此过程。 除了减少死锁的可能性之外,这还有助于保持数据的一致性,这始终是一件好事。 让您的交易尽可能少。如果您要向表中插入新行并相应地更新其他表,您可能希望使用触发器而不是另一个 SQL 语句来执行此操作。这同样适用于读取行和值。此类事情可以在您的交易之前或之后完成。

选择正确的隔离级别。可能的隔离级别是:

READ_UNCOMMITTED READ_COMMITTED 可重复阅读 可序列化

Sequelize 的official documentation 描述了如何自己设置隔离级别和锁定/解锁事务。

正如我所说,在没有进一步了解您的数据库和查询设计的情况下,我现在只能为您做这一切。 希望这会有所帮助。

【讨论】:

【参考方案2】:

这可能不是您问题的直接答案,但也许通过查看您遇到此问题的原因也会有所帮助。

1) doSomething() 有什么作用?无论如何我们可以在那里做一些改进?

首先,需要 60 秒的事务是可疑的。如果您将表锁定这么长时间,则很有可能应该重新访问设计。给定一个典型的数据库操作运行 10 - 100 毫秒。

理想情况下,所有数据准备都应该在事务之外完成,包括从数据库读取的数据。并且事务实际上应该只用于事务性操作。

2) 是否可以使用mysql存储过程?

确实,mysql 的存储过程没有编译为 Oracle 的 PL/SQL。但它仍在数据库服务器上运行。如果您的应用程序真的很复杂,并且在该事务中包含大量反向和强制数据库和您的节点应用程序之间的网络流量,并且考虑到有如此多的 javascript 调用层,它可能真的会减慢速度。如果 1) 不能为您节省很多时间,请考虑使用 mysql 存储过程。

显然,这种方法的缺点是在 nodejs 和 mysql 中都很难维护代码。

如果 1) 和 2) 绝对不可能,您可以考虑使用某种流量控制或排队工具。要么您的应用程序确保第二个请求在第一个请求完成之前不会发送,或者您有一些第 3 方排队工具来处理它。看来您在运行这些请求时不需要任何并行性。

【讨论】:

谢谢!排队工具做到了!我已经设置了一个bluebird-queue,现在所有的请求一个接一个地运行,没有阻塞。

以上是关于在 Node.js 中处理回滚的 MySQL 事务的主要内容,如果未能解决你的问题,请参考以下文章

ROLLBACK语句只能针对未提交的事务进行回滚操作,已提交的事务是不能回滚的?

MySql事务无法回滚的原因

MySql事务无法回滚的原因

MySQL表类型MyISAM/InnoDB的区别(解决事务不回滚的问题)(转)

Service中事务不能回滚的解决方式(转)

JPA中事务回滚的问题