- A+
事务是一组原子性的 SQL 查询,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。
ACID 表示原子性 (atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。一个运行良好的事务处理系统,必须具备这些标准特征。
原子性(atomicity)
一个事务必须被视为一个不可分割的最小单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务而言,不可能只执行其中的一部分操作,这就是事务的原子性。
一致性(consistency)
数据库总是从一个一致性的状态转换到另一个一致性的状态。
隔离性(isolation)
通常来说,一个事务所做的修改在最终提交之前,对其他事务是不可见的。
持久性(durability)
一旦事务提交,则其所做的修改就会永久保存在数据库中。
隔离级别
隔离性其实比想象的要复杂。在 MySQL 标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务外是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。
下面简单介绍一下四种隔离级别。
READ UNCOMMITED (未提交读)
在 READ UNCOMMITED 级别,事务中的修改,即使没有提交,对其他事务也是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能问题上来讲,READ UNCOMMITED 不会被其他的级别好太多,但是缺乏其他级别的好处,在实际应用中一般很少用到。
READ COMMITED(提交读)
大多数数据库系统的默认隔离级别都是 READ COMMITED (但 MySQL 不是)。READ COMMITED 满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务 从开始到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也被称为不可重复读(nonrepeatable read),因为两次执行相同的查询,可能会得到不一样的结果。
REPEATABLE READ(可重复读)
REPEATABLE READ 解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一样的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom Read)问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务也在该范围内插入了一条新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row)。InnoDB 和 XtraDB 存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。
可重复读是 MySQL 默认的事务隔离级别。
SERIALIZABLE (可串行化)
SERIALIZABLE 是最高的隔离级别,它通过强制事务串行执行,避免前面所说的幻读问题。简单来说,SERIALIZABLE 会在读取每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。
ANSI SQL 隔离级别:
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
READ UNCOMMITED | Yes | Yes | Yes | No |
READ COMMITED | No | Yes | Yes | No |
REPEATABLE READ | No | No | Yes | No |
SERIALZABLE | No | No | No | Yes |
死锁
死锁是指两个事务或者多个事务在同一资源的相互占用,并请求锁定对方占用的资源。从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。
为了解决这个问题,数据库系统实现了各种死锁检测和死锁超时机制。
锁的行为和顺序是和存储引擎有关的。以相同的顺序执行语句,有些存储引擎会产生死锁,有些则不会。
事务日志
事务日志可以帮助提高事务的效率。使用事务日志,存储引擎在修改表时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不是每次都把修改的数据本身持久到磁盘上。事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序 I/O,而不像随机 I/O 需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快得多。
MySQL 中的事务
MySQL 提供了两种事务型的存储引擎:InnoDB 和 NDB Cluster。
自动提交(AUTOCOMMIT)
MySQL 默认采用自动提交(AUTOCOMMIT)模式。也就是说,如果不是显式地开始一个事务,则每个查询都会被当做一个事务执行提交操作。在当前连接中,可以通过设置 AUTOCOMMIT 变量来启用或禁用自动提交模式。
mysql > set AUTOCOMMIT = 1;
1或者 ON 表示启用,0或者 OFF 表示禁用。当 AUTOCOMMIT = 0 时,所有的查询都是在一个是事务中,直到显式的执行 COMMIT 提交或者 ROLLBACK 回滚,该事务结束。同时又开始了另一个新事务。修改 AUTOCOMMIT 对非事务型的表,比如 MyISAM 或者内存表,不会有任何影响。对这类表来说,没有 COMMIT 或者 ROLLBACK 的概念,也可以说是相当于一直处于 AUTOCOMMIT 启用的模式。
在事务中混合使用存储引擎
MySQL 服务层不管理事务,事务是由下层的存储引擎实现的。所以在同一个事务中,使用多种存储引擎是不可靠的。
如果在事务中混合使用了事务型和非事务型的表(例如 InnoDB 和 MyISAM 表),在正常提交的情况下不会有任何问题,但如果该事务需要回滚,非事务表上的变更是无法撤销的。所以,为每张表选择合适的存储引擎非常重要。
隐式和显示锁定
InnoDB 采用的两阶段锁定协议(two-phase locking protocol)。在事务执行过程中,随时都可以执行锁定,锁只有在执行 COMMIT 和 ROLLBACK 的时候才会释放,并且所有的锁都是在同一时刻被释放。