# 系列目录
# 参考
- Isolation (database systems) - Wikipedia (opens new window)
- MySQL :: MySQL 8.0 Reference Manual :: 15.7.4 Phantom Rows (opens new window)
- 13 Data Concurrency and Consistency - Oracle Docs (opens new window)
# 事务概念
数据库事务 (transaction) 是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。
最常见的例子是转帐:从帐户 A 转 100 元到帐户 B 上,该事务的操作有:
- A 账户中减 100 元;
- B 账户中加 100 元。
这两个步骤,要么全做,要么全不做。
事务全做完以后,需要进行提交(commit)。提交以后,事务做的修改就会被永久记录在数据库中,即使发生故障也能恢复。
如果事务做到一半,不想做了/做不下去了,可以回滚(roll back),回到什么都没做的状态。
# 事务的 ACID
ACID,是指数据库管理系统 (DBMS) 在写入或更新资料的过程中,为保证事务 (transaction) 是正确可靠的,所必须具备的四个特性:
- 原子性 (atomicity):一个事务要么全做要么全不做;InnoDB 通过 undo log 保证回滚时能找到之前的数据。
- 一致性 (consistency):任何时刻,数据处于一种有意义的状态,这种状态是语义上的而不是语法上的。还是转账的例子:从帐户 A 转 100 元到帐户 B 上,如果帐户 A 上的钱减少了,而帐户 B 上的钱却没有增加,那么我们认为此时数据处于不一致的状态;InnoDB 通过 crash recovery 和 double write buffer 保证。(todo: cache recovery, double write buffer)
- 隔离性 (isolation):一个事务不影响其他事务的运行效果;下面会详细聊。
- 持久性 (durability):事务一旦提交,则其结果就是永久性的,即使故障也能恢复;InnoDB 通过 redo log 保证提交的数据一定不丢失。
# 事务的原子性和持久性的保证
- 将所有事务开始、提交、终止,以及数据的更新操作(记录数据更新前的值即前像,或更新后的值即后像)计入 log
- 系统崩溃后重启,先读取日志对已提交的事务进行 redo(保证持久性)
- 然后对尚未提交的的事务进行 undo(保证原子性)
# InnoDB 事务隔离级别
SQL 标准定义了四种隔离,隔离程度由低到高依次为:
- 读未提交/脏读(Read Uncommitted):事务会查询到其他未提交事务影响的数据,也被称为脏读;
- 读提交/不可重复读(Read Committed,Oracle 默认):事务只会查询到其他已提交事务影响的数据;但多次查询同一个数据,其结果可能变化(如果一个事务两次读同一个数据之间,某个事务对这个数据修改并提交了,读事务会发现两次读到的东西不一样),这就被称为不可重复读。
- 可重复读(Repeatable Read,InnoDB 默认,Oracle 不支持):事务只会查询到在当前事务开始前已提交事务影响的数据;但在插入数据时会查询到当前事务开始后提交的事务影响的数据(当前事务查询到不存在
id=1
的数据,过程中其他事务增加id=1
的数据并提交,当前事务插入id=1
时报错冲突,但的确又查不到id=1
的数据),被称为幻读。 - 序列化(Serializable):如果要彻底完全解决上面的三种问题,就只能让事务串行化了,也就是把多个事务变成一个序列。
从数据一致性的角度来讲,隔离程度越高越好;但是性能的角度来讲,隔离程度越高,事务并发度更低,且更容易死锁。所以,在实际中可能需要根据实际情况,选择性能和隔离性的一个平衡点。
有/无问题 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 有 | 有 | 无 |
读提交 | 无 | 有 | 无 |
可重复读 | 无 | 无 | 有* |
序列化 | 无 | 无 | 无 |
* InnoDB 提供了在可重复读下回避幻读的方法:如果在可重复读下进行了 SELECT ... FOR UPDATE
或 UPDATE
或 DELETE
,InnoDB 会锁住当前最新已提交的数据(而不是当前事务开始前已提交的数据),并在查询、更新、删除过程中使用最新已提交的数据。(代价是,加锁后读的值和加锁前读的值不同,即不可重复读)。-- locking - How MVCC works with Lock in MySql? - Stack Overflow (opens new window)