在 MySQL 中,SET autocommit=1 和 START TRANSACTION 有什么区别?(我是否漏掉了些什么?)

102

我正在阅读有关MySQL事务的内容,不确定是否已经正确掌握了某些特定内容,并且想要确保自己理解正确,以下是问题。我知道事务应该是做什么的,只是不确定我是否正确地理解了语句语义。

所以,我的问题是:下面的内容是否有任何错误(如果有的话,是什么错误):

默认情况下,MySQL启用了自动提交模式。

现在,SET autocommit = 0;将开始一个事务,SET autocommit = 1;会隐式提交。可以使用COMMIT;ROLLBACK;,在这两种情况下,autocommit在之后仍然设置为0(并且会隐式启动新的事务)。

START TRANSACTION;基本上会执行SET autocommit = 0;,直到发生COMMIT;ROLLBACK;

换句话说,START TRANSACTION;SET autocommit=0;是等价的,除了START TRANSACTION;相当于在COMMIT;ROLLBACK;之后隐式添加SET autocommit=1;之外。

如果是这样,那么我就不明白http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html#isolevel_serializable - 因为具有隔离级别意味着有一个事务,这意味着autocommit应该已经关闭了吗?

如果有除上述描述之外的其他区别(开始事务和设置autocommit之间的区别),是什么?

4个回答

100

了解数据库事务(自动提交、显式和隐式)处理可以避免因数据丢失而需要从备份中恢复。

事务控制数据操作语句,以确保它们是原子的。"原子"意味着事务要么发生,要么不发生。向数据库发出事务完成的唯一方法是使用COMMITROLLBACK语句(根据ANSI-92标准,未包含创建/开始事务的语法,因此具体实现由厂商决定)。COMMIT应用在事务中进行的更改(如果有)。ROLLBACK忽略事务中发生的所有操作——当UPDATE/DELETE语句执行了意外操作时非常有用

通常,单个DML(Insert、Update、Delete)语句在自动提交事务中执行——只要语句成功完成,它们就会被提交。这意味着在像你这样的情况下,没有机会将数据库回滚到运行该语句之前的状态。当出现问题时,唯一可用的还原选项是从备份中重建数据(如果存在备份)。在MySQL中,InnoDB默认情况下启用自动提交,MyISAM不支持事务。可以通过使用以下方式禁用自动提交:

SET autocommit = 0

显式事务是指语句被包含在明确定义的事务代码块中 - 对于MySQL,这是START TRANSACTION。它还需要在事务结束时明确执行COMMITROLLBACK语句。嵌套事务超出了此主题的范围。

隐式事务与显式事务略有不同。隐式事务不需要明确定义事务。但是,像显式事务一样,它们需要提供COMMITROLLBACK语句。

结论

显式事务是最理想的解决方案 - 它们需要语句COMMITROLLBACK来完成事务,并且正在发生的事情清楚地说明给其他人阅读,如果需要的话。如果与数据库交互,隐式事务是可以的,但应该只在测试结果并彻底确定为有效后才指定COMMIT语句。

这意味着您应该使用:

SET autocommit = 0;

START TRANSACTION;
  UPDATE ...;

...只有在结果正确时才使用COMMIT;

话虽如此,UPDATE和DELETE语句通常只返回受影响的行数,而不是具体细节。将这些语句转换为SELECT语句并审查结果以确保正确性之前尝试UPDATE/DELETE语句。

附录

DDL(数据定义语言)语句会自动提交 - 它们不需要COMMIT语句。即:表、索引、存储过程、数据库和视图创建或更改语句。


16
哇,速度真快 :-) 非常感谢! 我不太明白为什么需要在上面的例子中设置autocommit = 0;。开始一个事务不是已经意味着这一点了吗?如果不是,有什么区别? - ralokt
1
@tkolar:禁用自动提交会强制每个人使用START TRANSACTION;但并不是每个人都知道应该使用它。当然,另一个DBA可能也会重新启用它... - OMG Ponies
3
我觉得使用 SET autocommit = 0; 只是一种偏好,以免忘记使用事务。 - Timo Huovinen
当我通过 START TRANSACTION 明确启动事务时,即使自动提交已开启,我也必须明确指定 COMMIT。是这样吗? - tomwang1013
在嵌套的存储过程调用中,我应该使用 @OMGPonies。 - selvakumar
显示剩余2条评论

31
InnoDB中,您可以使用START TRANSACTION;,这是该引擎中官方推荐的进行事务处理的方式,而不是SET AUTOCOMMIT = 0;(除非用于优化只读事务,请勿在InnoDB中使用SET AUTOCOMMIT = 0; 进行事务处理)。 使用COMMIT;进行提交。
您可能希望在InnoDB中使用SET AUTOCOMMIT = 0;进行测试,而不是为了进行事务处理。
MyISAM中,您没有START TRANSACTION;。 在此引擎中,对于事务处理,请使用SET AUTOCOMMIT = 0;。 使用COMMIT;SET AUTOCOMMIT = 1;进行提交(在下面的MyISAM示例评论中解释了差异)。 您也可以使用这种方式在InnoDB中进行事务处理。
来源:http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_autocommit 一般使用事务处理的示例:
/* InnoDB */
START TRANSACTION;

INSERT INTO table_name (table_field) VALUES ('foo');
INSERT INTO table_name (table_field) VALUES ('bar');

COMMIT; /* SET AUTOCOMMIT = 1 might not set AUTOCOMMIT to its previous state */

/* MyISAM */
SET AUTOCOMMIT = 0;

INSERT INTO table_name (table_field) VALUES ('foo');
INSERT INTO table_name (table_field) VALUES ('bar');

SET AUTOCOMMIT = 1; /* COMMIT statement instead would not restore AUTOCOMMIT to 1 */

3

https://dev.mysql.com/doc/refman/8.0/zh-cn/lock-tables.html

在处理类似InnoDB表这样的事务性表时,使用LOCK TABLES和UNLOCK TABLES的正确方式是通过SET autocommit = 0(而非START TRANSACTION)开始一个事务,然后使用LOCK TABLES,并在显式提交事务之前不要调用UNLOCK TABLES。例如,如果您需要向表t1写入并从表t2读取,可以执行以下操作:

SET autocommit=0;
LOCK TABLES t1 WRITE, t2 READ, ...;... do something with tables t1 and t2 here ...
COMMIT;
UNLOCK TABLES;

2
有人知道为什么表锁只能在 autocommit=0 的情况下工作,而不能在 START TRANSACTION 下工作吗?这对我来说似乎完全是武断的。是否有技术原因? - jlh

2
如果您想使用回滚操作,则使用开始事务,否则忘记所有这些东西,
默认情况下,MySQL自动提交对数据库的更改。
要强制MySQL不自动提交这些更改,请执行以下操作:
SET autocommit = 0;
//OR    
SET autocommit = OFF

为了显式启用自动提交模式:

SET autocommit = 1;
//OR    
SET autocommit = ON;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接