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 查询的情况下回滚吗?的主要内容,如果未能解决你的问题,请参考以下文章