MySQL 事务可以在没有 ROLLBACK 查询的情况下回滚吗?

Posted

技术标签:

【中文标题】MySQL 事务可以在没有 ROLLBACK 查询的情况下回滚吗?【英文标题】:Can MySQL transaction rollback without ROLLBACK query? 【发布时间】:2017-07-01 04:11:57 【问题描述】:

我正在开发一个金融系统,但我遇到了 mysql 事务的问题。

该系统是一个简单的证券交易所,用户可以在其中买卖虚拟股票。为了保持买卖过程的完整性,我使用交易。 问题是在某些情况下(我不知道它取决于什么)一些事务被回滚(或未提交),但处理下一个查询。

流程如下:

    用户想以 1000 美元购买股票 在订单中有 4 个报价为 250 美元 START TRANSACTION 对于每个报价: 脚本执行更新查询(将美元从一个用户转移到另一个用户并以相反的方式共享)。然后脚本 INSERTs 条目到历史表。 用户支付费用(更新余额)。 重复 5 和 6 以获得下一个报价。 COMMIT

现在关键部分 - 在某些情况下,从第 5 点开始的更改没有保存,但从第 6 点开始保存(我看到已支付费用,但历史记录中没有交易)。 在此交易期间我没有使用ROLLBACK,并且脚本没有中断(因为在这种情况下不会支付费用)。

如果没有ROLLBACK 查询,事务是否有可能回滚?或者 MySQL 可以只提交几个最新的查询而不是全部?

【问题讨论】:

不,事务应该始终是原子的。要么什么都做,要么什么都不做。 系统中的第一个漏洞是您在交易前(仅)检查了订单簿,因此它可能已被更改;例如订单 1 可能已被使用,仅留下 3 个未结报价,并且根据您的实际代码,步骤 5 可能不会执行任何操作,但步骤 6 可能仍会收取费用。但这只是一个问题,而且仅在您的概念中。您可能会做错很多其他事情(例如,使用 myisam,使用错误的事务级别,不锁定选择,...),所以这里可能还有其他问题。如果您使用真实货币/价值进行交易,您应该让顾问检查您的代码。 我没有在第一篇文章中写过这个(对不起),但我在开始处理之前使用SELECT FOR UPDATE 锁定了行。当我处理单个报价时,我会额外检查,如果报价仍然存在,用户有有效余额等。但是目前它是演示系统。 我仍然认为最有可能的怀疑是,即使在检查失败时不执行 5,您也只是执行了 6。并且您的支票 2 必须(也)发生在交易中。但是,如果您不添加实际代码(这对于 *** 来说可能太多,但您可以尝试 codereview)并且也不添加所有详细信息,例如 for update 或其他检查,我们无法告诉您错误行为出现在哪里从。但我们可以向您保证:错误出现在您的代码、数据或设置中。 (工作)事务不可能只是部分执行。 我假设 FOR UPDATE 步骤 3 之后?您是否在每个 SQL 之后检查错误,包括 COMMIT 【参考方案1】:

事务与否,您的客户端代码有责任验证您的所有 INSERT 或 UPDATE 查询是否成功完成,然后发出显式 ROLLBACK 或关闭与我们的 COMMIT 的连接以发出隐式 ROLLBACK。如果其中任何一个失败但您的代码继续运行,则这些查询将不会生效(因为它们失败了),但其余的会生效。

这是一个简化的例子:

mysql> create table test (
    ->     id int(10) unsigned not null,
    ->     primary key (id)
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> insert into test(id) values (1);
Query OK, 1 row affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test(id) values (2);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test(id) values (-3);
ERROR 1264 (22003): Out of range value for column 'id' at row 1

我们应该在这里回滚并中止,但我们没有。

mysql> insert into test(id) values (4);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
+----+
| id |
+----+
|  1 |
|  2 |
|  4 |
+----+
3 rows in set (0.00 sec)

预期 4 行,得到 3。

除此之外,在许多情况下您可能会收到不需要的 COMMIT 但不需要的 ROLLBACK 是我不确定会发生的事情,除非您终止带有待处理更改的会话。

【讨论】:

【参考方案2】:

很久没有问这个问题了,但问题实际上是我没有检查每个查询的SQL错误。

实际上在某些时候,当我应该回滚事务时,我没有这样做。

如果您正在寻找答案 - 请再次检查您是否在事务中测试所有查询以确保成功执行,并且不要相信您使用的框架会自动为您执行此操作(只需再次检查)。

【讨论】:

以上是关于MySQL 事务可以在没有 ROLLBACK 查询的情况下回滚吗?的主要内容,如果未能解决你的问题,请参考以下文章

mysql binlog 中的 为啥会有 rollback

MySQL 5.7复制的一个小bug-XA事务

事务嵌套问题总结-rollback-only异常

SQL语句如何rollback

MySQL数据库

MySQL事务-ROLLBACK,COMMIT用法详解