我需要让MySQL服务器在其客户端断开连接后立即回滚事务,因为每个客户端都是并发工作的。这个问题可以像这样重现(使用innodb表类型)
客户端A:
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的事务。
当您物理断开客户端时,您不会发送正常的断开连接(这会导致回滚),MySQL协议也不太好聊天,因此服务器永远不会知道客户端不在。与其他数据库系统相比,我认为这是协议中的一个缺陷,在其他数据库系统中,客户端和服务器的内部对话要多得多。
不管怎样。有两个变量可以更改。他们基本上都是一样的,但针对不同的客户。
第一个是wait_timeout,它由java或php等应用程序客户端使用。
另一个是interactive_timeout,它由mysql客户端使用(就像在测试中一样)
在这两种情况下,服务器都会在几秒钟后终止连接,并在这样做时回滚所有事务并释放所有锁。
这是为了讨论一些注释。请注意,这与部分意见不一致。我将使用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将得到一个类似于"插入"的错误;连接丢失";。重新开始交易。