第十三章 事务 必考
不可分割的一段动作。
如何并行保证逻辑完整性
原子性需求:系统必须保证所有操作在数据库中要么全部正确反映出来,要么完全不反应
持久性需求:对数据库的改变必须是永久的,即使出现系统故障也是如此.
一致性需求:隐形约束不被本次操作改变(转账后,A的余额和B的余额应该保持不变)
隔离性需求:A执行完 B还没执行完,然后T2事务来查询ReadA ReadB 输出A+B。不能被T2偷窥到。
原子性:失败了怎么办?要么都发生要么都不发生
一致性:约束谁来负责?如果总合变了如何保障? 通过隔离保证。
隔离性:不同事物互相抢占,读取怎么办?
持久性:提交后能不能算数?
考点 ☆ 连接操作与其体现的性质

事务状态
活跃 Active------开始执行
部分提交 Partially committed------内存已经修改了,但是还没在磁盘中写入(逻辑完成,物理没完成)
提交 Commited------如果部分提交之后全部落实在磁盘上,就提交成功
失效 Failed------如果在活跃或部分提交的状态下,死锁 断电了 违背一致性约束 就失效。下一步会落实Rollback 回滚,撤出做了一半的操作。
中止 Aborted------回滚户后就是终止状态。 两种解决方案:重启事务(物理断电导致的);杀死kill事务(除零导致的,重启也过不了)。

并发执行
事务是由CPU计算和磁盘IO交织而成,不能CPU-IO-CPU-IO。
目标:要提高吞吐率和资源利用率(不能CPU-IO-CPU-IO)
减少等待时间
为了让并发不出错要调度
调度 ☆ 必考一道调度大题
指令在系统的执行顺序
规则:
一个事务内的顺序不能颠倒!其他事务的指令可以插进来
一组事务的一个调度必须包含其全部指令!

串行的顺序不同,可能导致结果不同,但是都符合一致性

左侧是正常的,同一个对象读写执行完才进行另一个事务,此时已经write落实在磁盘中,读取是没问题了。
但是右侧,还没落实writeA,却读取了,就读取到错误的内容。
【只有被read write落盘内的修改是正常的】

可串行化 很乱的调度通过交换顺序可以得到串行调度
假设每个事务都可以保持数据库的一致性。
因此串行化事务一定你保障数据库一致性
对于一个并行调度,如果能证明其等价于一个串行调度,那么就不会冲突。
☆ 冲突可串行化
事务的简化表达
事务简化表示------只保留readwrite

冲突------二者访问同一个数据;且有一人修改了(访同写)

就是有冲突就不能换顺序,如果不发生冲突就可以随意交换顺序。
冲突等价
交换顺序,不产生冲突,是冲突等价的。
怎么交换(交换同一个字母的wr时会冲突)
冲突可串行化
如果能通过冲突等价(交换)变成一个串行调度,那么说原来的并行调度是冲突可串行化。
证明S3是冲突可串行化的(通过冲突交换,把T1往上集中,T2往下集中)

怎么做?
不同变量直接集中,T内不能交换顺序。同一变量单独看。

冲突可串行化(保障过程一致性,导致结果一致性)
☆ 考点 视图可串行化(保障结果一致性)
允许环存在!
冲突可串行化太严格了,有时候只要求结果一直即可。
谁读了初始值?
对于 T第一次读它,对于所有的视图等价中都要求T来读第一次
谁读了谁写的?
T2依赖了T1的值,T2的Read依赖于T1的Write。我们就标记T1->T2,T2依赖于T1.
换一个调度,也要求T2依赖于T1.
谁最后收尾?
对于 T最后一次写它,对于所有的视图等价中都要求T来收尾。
关键在盲写:事务不读就写
做题------优先图 解决 可串行化检测(是在T事务中的) 6个节点
如何画图?先把对于每个数据的WR写成一行,然后看看前后1两个操作能否交换,不能交换说明存在依赖,就画出从前到后的边
如果最后出现环说明不是可串行化调度。
如果不出现环,就可以通过拓扑排序写出所有的调度顺序。

如果没有冲突,就不会有连边,如果有冲突且t1先访问,t2后访问,就画从t1-t2的边。
如果T1先读,T2后读,那么意思就是T1必须排在T2前面,是无法交换的!
上面就是无法冲突可串行化的例子。
一个调度可串行化的条件是当且仅当优先图中无环
首先画出优先图,然后进行拓扑排序,每一种拓扑排序都是一种调度顺序。


实际上只有三个对象,我们分别对ABC按时间顺序写出来
A: R1(A) W1(A) R2(A) W2(A) R3(A)
逐取两个看,RW1内部顺序不会变,不用考虑。W1(A) R2(A)不能换,所以T1->T2.
W1(A) W2(A)有冲突
W1(A) R3(A)有冲突 T1->T3
W2(A) R3(A)有冲突 T2->T3
B:R1(B) W1(B) R3(B)
T1->T3
C:R4(C) W4(C) R2(C) W2(C)
T4->T2
最终优先图:


冲突可串行化检测
将对同一个数据的操作按顺序写成一行:
r27(Q) w28(Q) w27(Q) w27(Q)
依次对比两个操作,看看是否能交换
如果有人写了就是冲突不可串行化的!
视图可串行化检测(考查2分)
T27第一个读
T29最后一个写
因此顺序就是T27 T28 T29
谁读了谁写的(依赖)
可恢复调度 考查是否是可恢复的 ☆
如果在并发过程中出问题了
【用别人的,必须比别人后提交】如果T9读取了T8修改过的数据,那么T9的commit要在T8之后。

比如T9提交了涉及T8修改的数据,结果T8还没执行完时报错了read(B),那么就无法回滚了,因为T9拿到的脏数据无法恢复了。
级联回滚(不太可能考 但工作可能遇到)
T12读了T11的数据,T11的数据来自于T10,导致T12依赖T11依赖T10,如果T10abort了,那么三个事务都要回滚

无级联调度(比可恢复更严格)
T9如果要用T8,那么必须在T8提交后使用,这样就不会引发级联崩溃。

可交换?可冲突可串行化?
对于一个并行调度,如果能证明其等价于一个串行调度,那么就不会冲突。
冲突可串行化(通过冲突等价交换 将并行变为串行证明可行)
可恢复调度(对commit的位置有要求)
级联调度(可能级联崩溃)
无级联调度(对commit有要求,且要求commit后读依赖数据)
弱一致性

允许这些调度是非可串行化的。
一致性等级(从高到低)

可重复读
已提交读无法保障可重复读,可能需要读完提交后还要读,对于不同时间读会有问题。就是不可重复读。
可重复读:只要我读过的数据,别人在提交之前改不了(我上厕所,上锁别人用不了)
最稳的是可串行化。

1.同数据
3.一般都是,不是要找反例 反例是NP问题。
要证明是,就是找一个成立
1.找被多个写的数据BE,一个个找碾压关系(写-写,最后写对前面的写有碾压关系)
对B来说,有三条写,只有写和写才有依赖关系。(读写是没有依赖关系的!)
我们标定W6(B),因为是最后写的!
只有最后一个操作才对前面的操作有碾压关系。
所以有T1->T6 T2->T6。
对E来说
2.对于每个数据,找谁在没有写之前读了它(谁先读谁就要先读)
对于A,T2->T1.
3.谁读了谁刚刚写进去的数字(原先写着必须在读者前面,关系中也应该是写着必须在读者前面?)
T1->T5 W1(A) R5(A)
T4->T5 W4(E) R5(E)
T3->T4 W3(C) R4(C)
理论上 对于读者在写着前面也应该有约束??
写顺序的时候,由于必定有序,因此可以找到最先和最后
6只出现在右侧,因此是在最后
1只在左边,因此是最前
中间的关系满足即可。
存在,顺序是:
4.可恢复调度
确定生成-消费依赖关系,只要让T4先提交,T5后提交即可。
T4是生产者,T5是消费者,那么要求消费者提交在生产者之后。

(4)
中间写写是冲突,有明确顺序要求
中间的写写是不冲突的,只要求最后的写能覆盖前面的数据即可
(5)可恢复调度依赖于生产关系,(写读关系)因此是可恢复的。
无极连:也要求读,有读写才有依赖关系,有连续依赖关系才会出现无极连问题

(2)可恢复调度:依赖于生产消费关系
要求commit关系:T1要在T2之前,T2在T3之前
(3)无极连(站短依赖):有依赖关系的话,w2 r3 T2的commit在T3的read的之前,写完立马提交,这样就能斩断cascade链条。
(4)级联回滚
由于不满足无极连,导致事务1回滚后,T2T3已经提交了,压根没法回滚了。
(5)
1.C1->C2->C3
(6)
w1(A) c1 r2(A) w2(B) c2 r3(B) c3
(7)
只满足未提交读

总结
可恢复调度:依赖于生产消费关系
要求commit关系:T1要在T2之前,T2在T3之前
无极连(斩断依赖,在写后立马comiited')