调度及可串行性
事务调度(schedule):
- 一组事务的基本步(读、写、其他控制操作如加锁、解锁等)的一种执行顺序称为对这组事务的一个调度。
- 并发(或并行)调度:多个事务从宏观上看是并行执行的,但其微观上的基本操作(读、写)则是交叉执行的。
- 并发调度的正确性:当且仅当在这个并发调度下所得到的新数据库结果与分别串行地运行这些事务所得的新数据库完全一致,则说调度是正确的。
可串行性:
-
如果不管数据库初始状态如何,一个调度对数据库状态的影响都和某个串行调度相同,则我们说这个调度是可串行化的(Serializable)或具有可串行性(Serializability)。
-
可串行化调度一定是正确的并行调度,但正确的并行调度,却未必都是可串行化的调度
- 并行调度的正确性是指内容上结果正确性,而可串行性是指形式上结果正确性,便于操作
- 比如上例只要把 B-20 改为 B-0 那么结果就是正确的,但是形式仍然不正确,先后顺序仍然有冲突。
-
可串行化的等效串行序列不一定唯一。
表达事务调度的一种模型
-
rT(A): 事务T读A。
-
wT(A):事务T写A
-
示例
T1:r1(A);w1(A);r1(B);w1(B)
T2:r2(A);w2(A);r2(B);w2(B)
冲突:
- 调度中一对连续的动作,它们满足:如果它们的顺序交换,那么涉及的事务中至少有一个事务的行为会改变。
- 有冲突的两个操作是不能交换次序的,没有冲突的两个事务是可交换的
- 几种冲突的情况:
- 同一事务的任何两个操作都是冲突的
- ri(X);wi(Y);wi(X);ri(Y)
- 不同事务对同一元素的两个写操作是冲突的
- wi(X);wj(X)
- 不同事务对同一元素的一读一写操作是冲突的
- wi(X);rj(X)
- ri(X);wj(X)
- 同一事务的任何两个操作都是冲突的
冲突可串行性:
-
一个调度,如果通过交换相邻两个无冲突的操作能够转换到某一个串行的调度,则称此调度为冲突可串行化的调度。
-
示例
- r1(A); w1(A); r2(A); w2(A); r1(B); w1(B); r2(B); w2(B)
- -> r1(A); w1(A); r1(B); r2(A); w2(A); w1(B); r2(B); w2(B)
- -> r1(A); w1(A); r1(B); w1(B); r2(A);w2(A); r2(B); w2(B)
-
冲突可串行性 是比 可串行性 要严格的概念
- 满足冲突可串行性,一定满足可串行性;反之不然。
- 冲突可串行性 ⫅ \subseteqq ⫅可串行性并发调度的正确性 ⫅ \subseteqq ⫅可串行性
-
判别算法
-
构造一个前驱图(有向图)
-
结点是每一个事务Ti。如果Ti的一个操作与Tj的一个操作发生冲突,且Ti在Tj前执行,则绘制一条边,由Ti指向Tj, 表征Ti要在Tj前执行。
-
测试检查: 如果此有向图没有环,则是冲突可串行化的!
-
示例:逐个看每个操作对象,是否有事务在其上产生冲突
-
封锁并发控制
- 怎样产生一个正确的并发调度?
- 怎样产生一个冲突可串性化的调度?
锁”是控制并发的一种手段
- 每一数据元素都有一唯一的锁
- 每一事务读写数据元素前,要获得锁。
- 如果被其他事务持有该元素的锁,则要等待。
- 事务处理完成后要释放锁。
- Li(A) : 事务Ti 对数据元素A加锁
- Ui(A) : 事务Ti 对数据元素A解锁
锁本身并不能保证冲突可串行性。
-
锁为调度提供了控制的手段。
-
不同的协议
- 排他锁X (exclusivelocks):只有一个事务能读、写,其他任何事务都不能读、写
- 共享锁S (sharedlocks):所有事务都可以读,但任何事务都不能写
- 更新锁U (Updatelocks):初始读,以后可升级为写
- 增量锁I (Incrementallock):增量更新(例如A=A+x)区分增量更新和其他类型的更新
-
相容性矩阵
-
-
加锁/解锁时机
-
0级协议(0-LP)
- 有写要求的数据对象A加排他锁,不再访问后即刻解锁。可防止丢失修改,但允许脏读,允许重复读错误
-
1级协议(1-LP)
- 有写要求的数据对象A加排他锁,事务提交时刻解锁。可防止丢失修改,可恢复, 防止脏读,允许重复读错误
-
2级协议(2-LP)
- 有写要求的数据对象A加排他锁,事务提交时刻解锁。有读要求的数据对象B加共享锁,不再访问后即刻解锁。可防止丢失修改,防止脏读,不允许重复读错误。
-
3级协议(3-LP)
- 有写要求的数据对象A加排他锁,事务提交时刻解锁。有读要求的数据对象B加共享锁,事务提交时刻解锁。防止所有不一致性。(如幻读,解决幻读的方法是增加范围锁或者表锁)。
-
-
隔离性级别
- 读未提交(read uncommitted) —相当于0级协议
- 读已提交(read committed) —相当于1级协议
- 可重复读(repeatable read) —相当于2级协议
- 可串行化(serializable) —相当于3级协议
问题 含义 丢失更新(Lost Update) 当两个或多个事务选择同一行,最初的事务修改的值,会被后面的事务修改的值覆盖。 脏读(Dirty Reads) 当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。 不可重复读(Non-Repeatable Reads) 一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现和以前读出的数据不一致。 幻读(Phantom Reads) 一个事务按照相同的查询条件重新读取以前查询过的数据,却发现其他事务插入了满足其查询条件的新数据。 隔离级别 丢失更新 脏读 不可重复读 幻读 Read uncommitted × √ √ √ Read committed × × √ √ Repeatable read(默认) × × × √ Serializable × × × × -
封锁粒度
- 封锁粒度是指封锁数据对象的大小。
- 粒度单位:属性值 -> 元组 ->元组集合 ->整个关系 ->整个DB某索引项 ->整个索引
- 由前往后: 并发度小,封锁开销小;
- 由后往前: 并发度大,封锁开销也大。
两段封锁协议
-
读写数据之前要获得锁。每个事务中所有封锁请求先于任何一个解锁请求
- 这样可以保证冲突可串行性,所有对某个元素可能有冲突的操作都在当前事务先进行了,即通过转换可以变成串行的调度,而不是交叉调度。
- 保证了不会丢失更新,实际上如果写哪个元素才加锁还是会丢失更新。
- 其实如果一个数据只是读取,没有写操作,那么可以不提前加锁,但此时事务是可串行事务,但并不是冲突可串行。
-
两阶段:加锁段,解锁段。加锁段中不能有解锁操作,解锁段中不能有加锁操作
-
两段锁协议是可能产生“死锁”的协议!