21

我需要让 MySQL 服务器在其客户端断开连接后立即回滚事务,因为每个客户端同时工作。

在客户端 A上,可以像这样(使用 innodb 表类型)重现问题 :

START TRANSACTION;
SELECT MAX(ID) FROM tblone FOR UPDATE;
#... then disconnect your connection to the server

在客户端 B 上:

START TRANSACTION;
SELECT MAX(ID) FROM tblone FOR UPDATE;
#... lock wait time out will occur here

我已经设置了 MySQL 的服务器选项,innodb_rollback_on_timeout并且在两个客户端上都使用了 mysql 的客户mysql --skip-reconnect端。我在网络上使用一台服务器和两个客户端进行了尝试。我在线路后物理地断开了网络(拔下电缆)SELECT ... FOR UPDATE;。我需要让其他客户端能够立即tblone在事务上使用(锁定、更新),为此我认为服务器应该在客户端 A 断开连接后回滚客户端 A 的事务。

4

2 回答 2

23

当您在物理上断开客户端时,您不会发送正常的断开连接(这会导致回滚),并且 MySQL 协议不是很健谈,因此服务器永远不会知道客户端不存在。与客户端和服务器在内部进行更多对话的其他数据库系统相比,我认为这是协议中的一个缺陷。

反正。您可以更改两个变量。他们基本上做同样的事情,但针对不同的客户。

第一个是wait_timeout,它被 java 或 php 等应用程序客户端使用。

另一个是interactive_timeout,它由mysql客户端使用(如您的测试中一样)

在这两种情况下,服务器都会在几秒钟后终止连接,这样做时会回滚所有事务并释放所有锁。

于 2012-04-08T05:57:47.977 回答
1

这是讨论的一些评论。请注意,这与某些评论不一致。我将使用 INSERT 而不是 SELECT..FOR UPDATE 因为效果更明显。

让我们看一些不同的案例:

(1)无SQL+超时

START TRANSACTION;
do some SQL statement(s)
do no SQL for more than the timeout (before COMMITing)

由于下面详述的情况,请避免这种情况。解决方案:不要依赖 InnoDB 来帮助您处理长事务。

(2) 长时间运行的查询

START TRANSACTION;
do some SQL statement(s)
run an SQL query that takes more than the timeout
COMMIT;

一切都很好。只要服务器 (mysqld) 继续执行查询,超时就不适用。也就是说,超时“时钟”在每个 SQL 语句的末尾重新开始

(3)(自动重新连接)

START TRANSACTION;
INSERT ... VALUES (123);
    time passes; no SQL performed for longer than the timeout
    disconnect occurs
INSERT ... VALUES (456);
    auto-reconnect (because you have it ENabled);
    the INSERT proceeds
COMMIT;

123 将被回滚;456 将被插入。(同样 SELECT..FOR UPDATE 会丢失锁。)不好。解决方案是关闭“自动重新连接”。相反,检查错误并将断开连接错误视为事务的致命错误。(然后重新开始交易。)

INSERT 456 将在新事务中运行,由autocommit.

(4)(无自动重新连接)

START TRANSACTION;
INSERT ... VALUES (123);
    time passes; no SQL for longer than the timeout
    disconnect occurs
INSERT ... VALUES (456);
    NO auto-reconnect (because you have it DISabled)
COMMIT;

123 将被回滚。456 的 INSERT 会出现类似“连接丢失”的错误。重新开始交易。

于 2021-06-02T14:45:13.347 回答