MySQL中的事务
存储引擎是驱动如何从硬盘中存储和检索数据的软件。虽然MySQL传统上提供了许多支持事务的存储引擎,但InnoDB现在已经成为金标准,是推荐使用的引擎。这里描述的事务原语将基于InnoDB引擎中的事务。
理解AUTOCOMMIT
默认情况下,单个INSERT、UPDATE或DELETE语句会被隐式包装在一个事务中并在执行成功后立即提交,这称为自动提交(AUTOCOMMIT)模式。通过禁用此模式,可以在事务中执行一系列语句,并在结束时执行COMMIT提交事务或ROLLBACK回滚事务。
在当前连接中,可以使用SET命令设置AUTOCOMMIT变量来启用或禁用自动提交模式。启用可以设置为1或者ON,禁用可以设置为0或者OFF。如果设置了AUTOCOMMIT=0,则当前连接总是会处于某个事务中,直到发出COMMIT或者ROLLBACK,然后MySQL会立即启动一个新的事务。此外,当启用AUTOCOMMIT时,也可以使用关键字BEGIN或者START TRANSACTION来开始一个多语句的事务。修改AUTOCOMMIT的值对非事务型的表不会有任何影响,这些表没有COMMIT或者ROLLBACK的概念。
还有一些命令,当在活动的事务中发出时,会导致MySQL在事务的所有语句执行完毕前提交当前事务。这些通常是进行重大更改的DDL命令,如ALTER TABLE,但LOCK TABLES和其他一些语句也具有同样的效果。有关会导致自动提交事务的完整命令列表,请查看对应版本的官方文档。
MySQL可以通过执行SET TRANSACTION ISOLATION LEVEL命令来设置隔离级别。新的隔离级别会在下一个事务开始的时候生效。可以在配置文件中设置整个服务器的隔离级别,也可以只改变当前会话的隔离级别:
建议最好在服务器级别设置最常用的隔离,并且只在显式情况下修改。MySQL可以识别所有4个ANSI标准的隔离级别,InnoDB也支持这些隔离级别。
在事务中混合使用存储引擎
MySQL不在服务器层管理事务,事务是由下层的存储引擎实现的。所以在同一个事务中,混合使用多种存储引擎是不可靠的。
假设在事务中混合使用事务表和非事务表(例如,InnoDB和MyISAM表),如果一切顺利,事务将正常工作。如果需要回滚,则无法撤销对非事务表的更改。这会使数据库处于不一致的状态,可能难以恢复,并使整个事务问题变得毫无意义。所以,为每张表选择合适的存储引擎,并不惜一切代价避免在应用中混合使用存储引擎是非常重要的。
在非事务表中执行事务相关操作的时候,MySQL通常不会发出提醒,也不会报错。有时候只有回滚的时候才会发出一个警告:某些非事务表中的变更无法回滚。但在大多数情况下,对非事务表的操作都不会有提示。
最好不要在应用程序中混合使用存储引擎。失败的事务可能导致不一致的结果,因为某些部分可以回滚,而其他部分不能回滚。
隐式锁定和显式锁定
InnoDB使用两阶段锁定协议(two-phase locking protocol)。在事务执行期间,随时都可以获取锁,但锁只有在提交或回滚后才会释放,并且所有的锁会同时释放。前面描述的锁定机制都是隐式的。InnoDB会根据隔离级别自动处理锁。
另外,InnoDB还支持通过特定的语句进行显式锁定,这些语句不属于SQL规范:[9],[10]
MySQL还支持LOCK TABLES和UNLOCK TABLES命令,这些命令在服务器级别而不在存储引擎中实现。如果需要事务,应该使用支持事务的存储引擎。因为InnoDB支持行级锁,所以没必要使用LOCK TABLES。
LOCK TABLES命令和事务之间的交互非常复杂,并且在一些服务器版本中存在意想不到的行为。因此,本书建议,除了在禁用AUTOCOMMIT的事务中可以使用之外,其他任何时候都不要显式地执行LOCK TABLES,不管使用的是什么存储引擎。