数据库事务浅析

2020.07.28 16:30

事务要解决的问题

在真实的数据库环境中可能有各种出错的情况,例如:

业务逻辑往往有多步操作,比如 A 向 B 转账时,至少需要两个步骤,先从 A 账户中扣减金额,再给 B 账户增加金额,如果中途出现了错误,那么最后的结果可能是 A 账户钱变少了,而 B 账户却没有收到钱,这对于用户来说是不可接受的。

而要解决这种问题,数据库事务为我们提供了基本的可靠性保证,事务将应用程序的多个读、写操作捆绑在一起成为一个整体,一个事务要么成功(提交),要么失败(终止或回滚)。

事务的 ACID

事务所提供的安全保证即大家所熟知的 ACID,分别代表原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Duration)。

事实上,ACID 四种特性并不是平级关系,一致性是最基本的属性,只有实现了原子性(A)、隔离性(I)、持久性(D),才能保证一致性(C)。

实现原子性与持久性

由于写入的中间状态和系统崩溃都是无法避免的,为了保证原子性和持久性,只能在崩溃后采取补救措施,这种数据恢复操作被称为“崩溃恢复”(Crash Recovery,也有资料称作 Failure Recovery 或 Transaction Recovery)。

实现原子性主要靠的是日志,对数据库的所有更新操作都写入日志,如果事务执行中途发生系统崩溃,则可以通过回溯日志,将已经执行的操作撤销。这里的日志一般有两种分别是 redo 日志和 undo 日志,最常见的场景是,数据库系统崩溃后重启,此时数据库处于不一致的状态,必须先执行一个崩溃恢复的过程:

实现隔离性

原子性仅能保证在单事务下的一致性,不能保证多事务并发操作的一致性,所以此时需要引入隔离性。要实现隔离性主要是靠加锁实现的。

现代数据库都提供了三种锁:

在 MySQL 中还存在两种意向锁(Intention Locks),在原来的 X/S 锁之上引入了 IX/IS,IX/IS 都是表锁,用来表示一个事务想要在表中的某个数据行上加 X 锁或 S 锁。有以下两个规定:

锁的兼容关系如下:

XIXSIS
X
IX
S
IS

隔离级别

ISO 和 ANSI SQL 标准制定了四种事务隔离级别:

各个隔离级别能解决的并发一致性问题:

脏读不可重复读幻读
读未提交
读已提交
可重复读
可串行化

参考资料

  1. 『浅入浅出』MySQL 和 InnoDB
  2. 《凤凰架构》:本地事务
  3. 数据库系统原理
  4. 《数据密集型应用系统设计》:事务