数据库复习之——并发控制

本文详细介绍了数据库并发控制的重要性,包括事务的ACID特性,并发执行可能导致的三种不一致性问题:丢失修改、不可重复读和脏读。重点探讨了并发控制方法,如基于锁的并发控制,包括锁的类型、相容性矩阵和不同级别的事务隔离,以及基于时间戳和有效性确认的并发控制策略。这些方法旨在确保并发调度的正确性和可串行化,防止数据不一致性和死锁的发生。

一、SQL语句的执行过程

二、事务及其特性

1、事务的概念

事务是DBMS提供的控制数据操作的一种手段,程序员将一系列的数据库操作组合到一起,并作为一个整体进行操作和控制,以便DBMS能够提供一致性状态转换。

(1)事务的宏观性【程序员看到的事务】:

​ 一个存取或更新程序的一次执行,或者说是一条或多条SQL语句的一次执行。

(2)事务的微观性【DBMS看到的事务】:

​ 对数据的一系列基本操作的一个整体执行。

(3)事务的并发执行:即宏观上是并行执行的、但微观上的基本操作(读、写)是可以交叉执行的。

2、事务的特性(ACID)

(1)原子性Atomicity:事务的操作是不可分的,要么全做、要么全不做

(2)一致性Consistency:保证事务的操作状态是正确的,符合一致性操作规则,不能出现三种不一致性。

(3)隔离性Isolation:DBMS保证并发执行的事务之间不受影响。即:两个并发执行的事务T1和T2,相当于串行执行。

(4)持久性Durability:DBMS保证已经提交事务的影响是持久的,被撤销事务的影响是可恢复的。

三、并发的三种不一致性

1、丢失修改

T1和T2两个事务读取并修改同一数据,T2的提交结果破坏了T1的提交结果,导致T1的修改被丢失。

2、不可重复读*(读的是“旧”数据)*

事务T1读取数据之后,事务T2执行了更新操作,使T1无法再现前一次的读取结果。

不可重复读又包含了三种情况:

(1)事务T2对数据做了修改操作;

(2)事务T2对部分数据进行了删除操作;

(3)事务T3对数据进行了添加操作**(幻读)**

3、脏读*(读的是“新”数据)*

事务T1读取并修改数据,事务T2读到了T1修改后的数据,此时由于T1事务回滚(数据还原成原来的数据),导致T2读取的数据为“脏”数据。

四、并发控制方法

1、事务调度

  1. 事务的调度:一组事务的基本步骤(读、写、其他控制操作如加锁解锁等)的一种执行顺序称之为这组事务的一个调度。
  2. 并发(并行)调度:一组事务从宏观上是并行执行的,但其微观上是读写等多个基本操作的交叉执行。
  3. 调度又分为:串行调度和并发调度。
  4. 并发调度的正确性:多个事务在并发调度下得到的新数据库结果与这些事务在串行调度下得到的新数据库结果相同的时候,则说明调度是正确的。

两个问题:】(1)如何判断并发调度的正确性?(2)如何产生一个正确的调度?

2、可串行性

可串行性:不论数据库的初始状态如何,一个调度对数据库状态的影响都与某个串行调度相同,说明我们的调度是可串行化的或具有可串行性。

注意:

  1. 可串行化调度一定是一个正确的并行调度,而正确的并行调度不一定是一个可串行化的调度。【两者的区别:】从定义的角度:事物的串行调度与并行调度的结果相同表示为正确的并行调度;而事物的串行调度和并行调度对数据库状态的影响是相同的,说明为可串行化调度。另外:调度的正确性指内容上的结果正确,而可串行性指形式上结果正确,便于操作。

  2. 冲突:调度中一对连续的动作满足(如果它们的顺序交换,那么涉及到的事务中至少有一个事务的行为会改变)

  3. 有冲突的两个事务是不可交换次序的,没冲突的两个事务是可以交换的

  4. 几种冲突情况

    (1)同一个事务的两个任何操作都是冲突的:ri(X),wi(Y) wi(X),ri(Y)【在同一事务T中,读写不可互换;程序员规定的】

    (2)不同事务对同一个元素的两个写操作是冲突的:wi(X),wj(X)
    (3)不同事务对同一个元素的一读一写操作是冲突的:wi(X),rj(X) ri(X),wj(X)【对同一个对象X,一读一写、双写都不可互换】

3、冲突可串行性

一个调度,如果通过交换相邻两个无冲突的操作,能够转换得到某个串行操作,那么该调度为冲突可串行化的调度

注意:

  • 冲突可串行化是比可串行性更加严格的概念;
  • 冲突可串行化的调度一定是可串行性调度,反之不然;

4、冲突可串行性判别算法

并发调度的正确性、可串行化、冲突可串行化三者之间的关系:并发调度的正确性 ⊇ \supseteq 可串行性 ⊇ \supseteq 冲突可串行化

判别算法:

  1. 构造一个前驱图(有向图)。
  2. 节点是每个事务 T i T_i Ti如果 T i T_i Ti 的一个操作与 T j T_j Tj 的一个操作发生冲突,且 T i T_i Ti T j T_j Tj 之前执行,则绘制一条边,由 T i T_i Ti 指向 T j T_j Tj ,表征 T i T_i Ti 要在 T j T_j Tj 前执行。
  3. 测试检查:如果此有向图无环,则是冲突可串行化的。

【只需要找出不同事务对同一个元素的双写、一读一写出现冲突的情况】

判别算法示例:

r2(A); r1(B); w2(A); r3(A); w1(B); w3(A); r2(B); w2(B)
w2(A)和r3(A)是冲突的; w1(B)和r2(B)是冲突的;
T1 → T2 → T3
不存在环,是冲突可串行化的调度

r2(A); r1(B); w2(A); r2(B); r3(A); w1(B); w3(A); w2(B)
w2(A) 和 r3(A) 发生冲突;r2(B) 和 w1(B) 发生冲突;w1(B) 和 w2(B) 发生冲突;
存在环,是非冲突可串行化的调度

5、并发控制方法(即:如何产生一个冲突可串行化的调度呢?)

1. 基于锁的并发控制{基于锁的方法}
a. 锁是一种控制并发的手段
  • 每一个数据元素都有唯一的锁
  • 每个事务读取数据之前都需要获得锁
  • 如果被其他事务持有该元素的锁,需要等待
  • 事务处理完成之后需要释放锁

Li(A):表示事务Ti对元素A加锁
Ui(A):事务Ti对元素A解锁

b. 锁协议之锁的类型
  • 排他锁X(eXclusive locks):数据只能被持有锁的事务进行读写操作,其他事务不能读、也不能写
  • 共享锁S(Shared locks):除了持有锁的事务可读可写外,其他事务只能读、不能写
  • 更新锁(Update locks):初始读,以后可升级为写
  • 增量所(Incremental locks):增量更新(A = A+x),要区分增量更新和其他类型的更新。
c. 锁协议之相容性矩阵


上述表示:在某事务持有数据元素A的S锁的时候,其他事务可以申请数据A的S锁,但不可申请A的排他锁;在某事务持有数据元素A的X锁的时候,其他事务不可申请A的任何锁;

上述表示:在某事务持有元素A的S锁的时候,其他事务可以申请A的S锁和U锁,但不可申请A的X锁;另外在某事务持有A的X锁或者U锁的时候,其他事务不可申请A的任何锁。

d. 锁协议之加锁\解锁时机

(1)0级协议(0-LP):

​ 对有写需求的数据对象A加排他锁,不再访问该元素之后,立刻解锁。可防止丢失修改,但允许脏读和不可重复读错误
在这里插入图片描述

(2)1级协议(1-LP):

​ 对有写需求的数据对象A加排他锁,在事务的提交时刻解锁。可以防止丢失修改,可恢复,防止脏读错误,但允许不可重复读错误

​	[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JaHOz4Fi-1638682236144)(C:\Users\ZhangLingRan\AppData\Roaming\Typora\typora-user-images\image-20211116161405643.png)]

(3)2级协议(2-LP):

​ 对有写需求的数据对象A加排他锁,在事务的提交时刻解锁。对有读需求的数据对象B加共享锁,不再访问后即刻解锁。可以防止丢失修改防止脏读错误不允许不可重复读错误

​	[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-suegLxjH-1638682236145)(C:\Users\ZhangLingRan\AppData\Roaming\Typora\typora-user-images\image-20211116162316156.png)]

(4)3级协议(3-LP):

​ 对有写需求的数据对象A加排他锁,在事务的提交时刻解锁。对有读需求的数据对象B加共享锁,在事务的提交时刻解锁。可以防止所有的不一致性,如幻读等

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FTu1dksD-1638682236145)(C:\Users\ZhangLingRan\AppData\Roaming\Typora\typora-user-images\image-20211116162643916.png)]

e. SQL标准定义的四个(事务)隔离级别
  1. 读未提交(read uncommitted):相当于0级协议,写时候Lx(A),写完立即Ux(A);

    最低的隔离级别,允许读取尚未提交的数据变更,不能防止重复度错误和脏读错误以及幻读错误。

  2. 读已提交(read committed):相当于1级协议,写的时候Lx(A),事务结束立即Ux(A);

    允许读取并发事务已经提交的数据,可以防止脏读,但不可是防止重复读和幻读。

  3. 可重复读(repeatable read):相当于2级协议,写的时候Lx(A),事务结束立即Ux(A);读的时候Ls(B),读完了即刻Us(B);

    对同一个字段的多次读取结果是一致的,除非数据被该事务本身所修改。可以防止脏读,防止重复读,但不能防止幻读。

  4. 可串行化(serializable):相当于3级协议,写的时候Lx(A),事务结束立即Ux(A);读的时候Ls(B),事务结束的时候立即Us(B);

    最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,事务之间完全不会被干扰,该级别可防止脏读、幻读、不可重复读错误。

隔离级别脏读不可重复读幻读
读未提交不可防止不可防止不可防止
读已提交可防止不可防止不可防止
可重复读可防止可防止不可防止
可串行化可防止可防止可防止
f. 锁协议之锁的粒度

锁的粒度指的是:封锁数据对象的大小。

粒度单位:属性值 → 元组 → 元组集合 → 整个关系 → 整个DB某索引 → 整个索引

由前往后:并发度小, 封锁开销小。

由后往前:并发度大, 封锁开销大。

g. 两段封锁协议(2PL)

定义:(1)读写数据之前要获得锁。每个事务中所有的封锁请求先于任何一个解锁请求。(2)两阶段:封锁、解锁。加锁阶段中不可有解锁操作,解锁阶段中不能有加锁操作。

【注意:】(1)两段封锁协议是可以保证冲突可串行性的!(2)两段封锁协议是可能产生“死锁”的协议!

那么如何判断死锁的发生,并且如何消除死锁呢?《操作系统》

2. 基于时间戳的并发控制{基于撤回的方法}
a. 基本概念

(1) 时间戳:基于时间的标志,将某个时刻转换成数值。时间戳具有唯一性和递增行。

(2)事务的时间戳:

  • 事务T启动时,系统将当前时刻赋予T,为T的时间戳;
  • 时间戳小的事务先执行,大的后执行;
  • 利用时间戳可以在不加锁的情况下来进行并发控制。
b. 用时间戳来进行并发控制的基本思路
  • 借用时间戳,将一组并发事务的交叉执行,强制等价于一个特定顺序的串行执行。
  • 所谓特定顺序即:按照时间戳由小到大
  • 如何强制:执行的时候判断冲突
    • 如果无冲突则予以执行
    • 如果有冲突则撤销事务,并重启该事务,此时该事务获得了一个更大的时间戳,表明是后执行的事务。
  • 有哪些冲突呢?
    • 读-写、写-读冲突
    • 写-写冲突
c. 基于时间戳的并发控制思想
  • 事务在启动时刻被赋予唯一的时间戳,表示其启动顺序。
  • 为每一个数据库元素保存读时间戳和写时间戳,以记录读取或写该数据元素的最后的事务。
  • 通过在事务读写数据时判断是否存在冲突(读写、写读、写写),来强制事务以可串行化的方式执行。
d. 调度规则

【一些规定】:

  • 事务的时间戳:TS(T),TS即TimeStamp

  • 对DB中的每个数据元素x,系统保留其上的最大时间戳

    • RT(x):即R-TimeStamp(x)

      即最后读x的事务的时间戳,读过该数据的事务中最大的时间戳。

    • WT(x):即W-TimeStamp(x)

      即最后写x的事务的时间错,写过该数据的事务中最大的时间错。

【一些调度规则】:

  • 读-写、写-读并发:
    • 若T事务读x,则将T的时间戳TS与WT(x)比较
      • 若TS大,则允许T操作,并且更改RT(x) = max{RT(x),TS};
      • 否则,有冲突,撤回T,并重启T
    • 若T事务写x,则将T的时间戳TS与RT(x)比较
      • 若TS大,则允许T操作,并且更改WT(x) = max{WT(x),TS};
      • 否则,有冲突,撤回T,并重启T
  • 写-写并发
    • 若事务T写x,将T的时间戳TS与WT(x)比较
      • 若TS大,允许T写, 并将WT(x)更改为T的时间戳
      • 否则,有冲突,T撤回,并重做

先执行的先操作,后执行的后操作,只有满足这一条件才能说明:无冲突。而重启事务也是为了满足这一点】

上述规则可以解决:T过晚的读,和过晚的写错误。

、

3. 基于有效性确认的并发控制{基于撤回的方法}
a. 基于有效性确认的并法控制基本思想
  • 事务在启动时刻被赋予唯一的时间戳,表示其启动顺序。
  • 为每一个活跃事务保存其读写数据的集合,RS(T):事务T读数据的集合;WS(T):事务T写数据的集合。
  • 通过对多个事务的读写集合,判断是否有冲突(存在事实上不可实现的行为),即有效性确认,来完成事务的提交与回滚,强制事务以串行化方式执行。
b. 基于有效性确认的调度器
  • 事务在启动时刻被赋予唯一的时间戳,以表示其启动顺序;
  • 为每一个活跃事务保存其读写数据的集合,RS(T):事务T读数据的集合;WS(T):事务T写数据的集合。
  • 事务分三个阶段进行:
    • 读阶段:事务从数据库中读取读集合 RS(T) 的所有元素,事务还在其局部地址空间计算它将要写的所有值;
    • 有效性确认阶段:调度器通过比较该事务与其他事务的读集合来确认该事务的有效性;
    • 写阶段:事务往数据库中写入其写集合中的元素的值;
  • 每个成功的事务是在其有效性确认的瞬间执行的
  • 并发事务串行的顺序即事务有效性确认的顺序。
c. 调度器维护的三个集合
  1. START集合:已经开始但尚未完成有效性确认的事务集合。对此集合中的事务,调度器维护START(T),即事务T开始的时间。

  2. VAL集合:已经确认有效,但尚未完成第三个阶段写的事务。对此集合中的事务,调度器维护START(T)和VAL(T),即T的确认时间。

  3. FIN集合:已经完成第三个阶段的事务,对这样的事务T,调度器记录START(T),VAL(T),FIN(T),即T完成的时间。

d. 存在的两种冲突

冲突一:假设事务T和事务U满足:

(1)U在VAL或FIN集合中,即U已经经过了有效性确认。

(2)FIN(U) > START(T), 即U在T开始之前没有完成。

(3)RS(T) ∩ \cap WS(U) ≠ ∅ \neq \emptyset = ,特别的,设其均包含数据库元素x

则T和U的执行存在冲突,T不应该进行有效性确认。

简单来讲,即如果一个较早的事务U现在正在写入T应该读过的某些对象,则T的有效性确认不能确认。

【将上述判断规则用通俗的语言来讲】:事务U已经进行了有效性确认,但在事务T开始的时候,U还未执行完,那么如果T的读集合与U的写集合的交集非空的话,那么U和T这两个事务存在冲突。

冲突二:假设事务T和事务U满足:

(1)U在VAL中,即U已经经过了有效性确认。

(2)FIN(U) > VAL(T), 即U在T进入有效性确认之前还没有完成。

(3)WS(T) ∩ \cap WS(U) ≠ ∅ \neq \emptyset = ,特别的,设其均包含数据库元素x

则T和U的执行存在冲突,T不应该进行有效性确认。

简单来讲,即如果T在有效性确认后,可能比一个较早的事务先写某个对象,那么T将不能进行有效性确认。

【将上述判断规则用通俗的语言来讲】:事务U已经进行了有效性确认,而在T进入有效性确认的时候U还未执行结束,如果T和U的写集合有交集的话,那么T将不能被有效性确认。

e. 有效性确认的规则
  1. 对于所有已经经过有效性确认,且在T开始之前还没有完成的U,即满足FIN(U) > START(T)的U。判断 R S ( T ) ∩ W S ( U ) RS(T) \cap WS(U) RS(T)WS(U) 是否为空。是空的则确认T,否则不确认T
  2. 对于所有已经经过有效性确认,且在T进入有效性确认之前还没有完成的U,即满足FIN(U) > VAL(T)的U。判断 W S ( T ) ∩ W S ( U ) WS(T) \cap WS(U) WS(T)WS(U) 是否为空。是空的则确认T,否则不确认T
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zhang L.R.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值