第 17 章:事务¶
事务的 ACID¶
原子性¶
原子性(Atomicity):事务中的操作要么全部执行成功,要么全部不执行,不能执行一半退出。
否则的话数据库就会失去一致性。
一致性¶
一致性(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 连一条单向边。
如果最后的图出现了环,那么这个调度就不是冲突可串行的。