跳转至

第 17 章:事务

事务的 ACID

原子性

原子性(Atomicity):事务中的操作要么全部执行成功,要么全部不执行,不能执行一半退出。

1
2
3
4
begin transaction;
  update account set credit = credit - 13000 where name = '帕路奇亚';
  update account set credit = credit + 13000 where name = '帝牙卢卡';
end transaction;

否则的话数据库就会失去一致性。

一致性

一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态;或者说事务按照预期生效,生效后数据的状态还是预期的状态。

隔离性

隔离性(Isolation):多个用户并发访问数据库的时候,多个并发事务之间要相互隔离。

持久性

持久性(Durability):一个事务一旦被提交,它对数据库中数据的改变就是永久性的。即使数据库崩了也不应该对其有任何影响。

事务的状态

  • 活跃(Active):事务正在执行。
  • 部分提交(Partially committed):事务的最后一项已经执行完,但是结果数据可能还在内存的缓冲区里。
  • 已提交(Committed)。
  • 失败(Failed):执行的时候出现了问题,要回滚。
  • 已中止(Aborted):事务已经回滚完了。

并发执行

调度

有两个事务前来执行。

T1 T2
read(A) read(A)
A := A - 50 temp := A * 0.1
write(A) A := A - temp
read(B) write(A)
B := B + 50 read(B)
write(B) B := B + temp
write(B)

因为 CPU 实际上只能序列地执行指令,所以我们需要把它们两个进行调度。

串行调度

比如这样:

T1 T2
read(A)
A := A - 50
write(A)
read(B)
B := B + 50
write(B)
read(A)
temp := A * 0.1
A := A - temp
write(A)
read(B)
B := B + temp
write(B)

串行的调度必能保持一致性,但低效(没法并发)。

并发调度

T1 T2
read(A)
A := A - 50
write(A)
read(A)
temp := A * 0.1
A := A - temp
write(A)
read(B)
B := B + 50
write(B)
read(B)
B := B + temp
write(B)

有些并发调度不能保持一致性。

可串行化

一个对于很多事务的串行调度可以保持一致性;一个调度如果和一个串行调度等价,那么它就是可串行化的。

冲突可串行化

如果有两个事务 i 和 j 要对同一个值进行修改,除了 i 和 j 都是读取这个值的情况都是冲突的。比如 i 要读 j 要写,交换它们之间的顺序会出问题。

所以如果两个操作是有冲突的,则二者执行次序不能交换。

冲突等价

如果调度 S 可以通过交换不冲突的指令获得 S',那 S 和 S' 就是冲突等价的。如果 S 和一个串行调度是冲突等价的,那么 S 就是冲突可串的。

视图可串行化

可恢复性

在并行执行事务的时候,事务内部可能会出现问题,要回滚;因为原子性的要求,这也会导致依赖它的其他事务(读了它写的内容)和它一起滚。

可恢复的调度应该满足:先写的事务先提交。当事务 j 要读事务 i 写的数据时,事务 i 必须要先于事务 j 提交,否则如果 i 崩了而 j 已经提交就无法完成 j 的回滚。

级联回滚

一个事务回滚会导致依赖它的其他事务和它一起回滚。

无级联调度

无级联调度:单个事务失效不产生级联回滚的调度。

对于事务 i 和 j,如果 j 要读 i 写的东西,那么 i 在 j 第一次读之前就要提交。这样 i 崩了

每个无级联调度也都是可恢复的。

可串行化测试

前驱图

如果 i 和 j 都访问了同一个数据,先访问的 i 朝后访问的 j 连一条单向边。

如果最后的图出现了环,那么这个调度就不是冲突可串行的。