MySQL提供了许多支持事务的存储引擎,但 InnoDB 现在已经成为推荐使用的存储引擎。而且从 MySQL5.5 版本开始,默认的存储引擎就从 MyISAM 变成了 InnoDB。所以接下来描述的事务都将基于InnoDB存储引擎。
事务的定义
事务是一组SQL语句作为一个工作单元以原子方式进行处理。
ACID特性
事务具有四个特性,分别是原子性、一致性、隔离性、持久性,这四个特性简称ACID特性。
原子性(Atomicity)
一个事务是一个不可分割的工作单元,事务中的所有操作要么全部成功提交,要么全部失败回滚。
事务的原子性是通过undo log来实现的。当事务对数据进行修改时,InnoDB会生成对应的undo log,当事务需要进行回滚时,InnoDB就会根据undo log恢复到事务开始执行之前的状态。
持久性(Durability)
事务一旦提交,事务所做的修改会永久保存在数据库中,此时即使数据库崩溃数据也不会丢失。实际上持久性也分很多不同的级别,有些持久性策略能够提供非常强的安全保障,而有些则未必。
事务的持久性是通过redo log来实现的。当事务提交时,InnoDB会把 redo log 写到磁盘中,如果数据库宕机,恢复后会读取 redo log 中的记录来恢复数据。
隔离性(Isolation)
一个事务的操作不会被其它事务影响。最理想的情况是是一个事务执行完成后再执行另一个事务,但处于性能上的考虑,一般都需要多个事务并发执行,这时就要求事务执行过程中不受到其他并发执行事务的影响。比如不能读取到另一个未提交事务所做的修改。
事务的隔离性是通过MVCC和锁机制来实现,将在之后的文章进行详细介绍。传送门:MySQL MVCC详解,MySQL 锁机制
一致性(Consistency)
事务执行前后,在业务上来看数据都是正确的。比如学生的学号不会有重复,所有账户的总额是不变的。
一致性是事务追求的最终目标,原子性、持久性和隔离性,都是为了保证数据库状态的一致性而存在的。此外,除了数据库层面的保障,一致性的实现也需要应用层面进行保障。
事务并发问题
在并发情况下,MySQL同时读写可能会导致三类问题:脏读、不可重复度和幻读。
脏读
一个事务读取到了另一个事务未提交的数据。
不可重复读
一个事务中两次查询同一条数据,但由于其他事务的修改导致两次看到的数据结果不一样。
幻读
一个事务中两次查询某个范围的数据,但查询结果的条数不一样。
事务隔离级别
事务隔离级别越高,并发程度就会越低。
READ UNCOMMITTED(读未提交)
在READ UNCOMMITED级别,存在脏读、不可重复读、幻读问题。从性能上来说不会比其他级别好太多,却缺乏其他级别的很多好处,在实际应用中一般很少使用。
READ COMMITTED(读已提交)
在READ COMMITED级别,存在不可重复读、幻读问题。大多数数据库系统的默认隔离级别是READ COMMITED(但MySQL不是)。
REPEATABLE READ(可重复读)
在REPEATABLE READ级别,存在幻读问题。REPEATABLE READ是MySQL默认的事务隔离级别。
SERIALIZABLE(串行化)
SERIALIZABLE是最高的隔离级别,该级别强制事务按序执行,使不同事务之间不可能产生冲突。实际应用中很少用到这个隔离级别,除非需要严格确保数据安全且可以接受并发性能下降的结果。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED(读未提交) | √ | √ | √ |
READ COMMITTED(读已提交) | × | √ | √ |
REPEATABLE READ(可重复读) | × | × | √ |
SERIALIZABLE(串行化) | × | × | × |
AUTOCOMMIT
默认情况下,MySQL处于自动提交(AUTOCOMMIT)模式。单个DML语句会被隐式包装在一个事务中并在执行成功后立即提交。也可以使用关键字BRGIN或者START TRANSACTION来开始一个多语句的事务。
在当前连接中,可以使用SET命令设置AUTOCOMMIT来启动或禁用自动提交模式。如果设置了AUTOCOMMIT=0,则当前连接总是会处于某个事务中,直到发出COMMIT或者ROLLBACK。
还有一些命令,当在活动的事务中发出时,会导致MySQL在事务的所有语句执行完毕前提交当前事务。这些通常是DDL命令,如ALTER TABLE,但LOCK TABLES和其他一些语句也具有同样的效果。
参考
《高性能MySQL(第4版)》