第10章 并发控制

多个事务同时存取共享的数据库时,如何保证数据库的一致性?

一、并发操作与并发问题

  1. 在多用户DBS中,如果多个用户同时对同一数据操作,称为并发操作。
  2. 并发操作引发的问题
    • 丢失更新
    • 脏读
    • 不一致分析

二、并发事务调度与可串性

1. 可串化调度

  • 调度:多个事务的读写操作按时间排序的执行序列
  • 调度中每个事务的读写操作保持原来顺序
  • 多个事务的并发执行存在多种调度方式

T1: r1(A) w1(A) r1(B) w1(B)
T2: r2(A) w2(A) r2(B) w2(B)
Sc = r1(A) w1(A) r2(A) w2(A) r1(B) w1(B) r2(B) w2(B)
Sa = r1(A) w1(A) r1(B) w1(B) r2(A) w2(A) r2(B) w2(B)

2. 冲突可串性

如果调度中一对连续操作是冲突的,则意味着如果它们的执行顺序交换,则至少会改变其中一个事务的最终执行结果

下面两个序列结果不同。第一个序列中,r2(B)读的是B的新值;第二个序列中,r2(B)读的是B的旧值。

r1(B) w1(B) r2(B) w2(B)
r1(B) r2(B) w1(B) w2(B)

下面两个序列结果就是相同的。

r1(A) w1(A) r2(B) w2(B)
r1(A) r2(B) w1(A) w2(B)

3. 优先图

优先图用于冲突可串性的判断
优先图结构

  • 节点:事务
  • 有向边:若Ti —> Tj,那么存在Ti中的操作A1和Tj中的操作A2满足
    • A1在A2前
    • A1和A2是冲突操作

三、锁与可串性实现

1. 二阶段锁(2PL)

  1. 事务读写数据前需要获得该数据上的锁。
  2. 事务释放锁后,将不再获得任何锁。

1. Exclusive Locks(X锁,也称写锁)

事务获得数据的X锁后才能对数据进行修改

2. Share Locks(S锁,也称读锁)

如果数据被加了S锁,那么其他事务无法获得该数据的X锁,但是可以获得S锁。事务获得数据的S锁后,如果需要修改数据,需要通过Update Lock把S锁升级为X锁

3. Update Lock(U锁,也称更新锁)

  1. 如果事务取得了数据R上的更新锁,则可以读R,并且可以在以后升级为X锁
  2. 单纯的S锁不能升级为X锁
  3. 如果事务持有了R上的Update Lock,则其它事务不能得到R上的S锁、X锁以及Update锁
  4. 如果事务持有了R上的S Lock,则其它事务可以获取R上的Update Lock

2.多粒度锁(MGL)

  • 粒度指加锁的数据对象的大小,可以是整个关系、块、元组、整个索引、索引项

  • 锁粒度越细,并发度越高;锁粒度越粗,并发度越低

  • 允许多粒度树中的每个结点被独立地加S锁或X锁,对某个结点加锁意味着其下层结点也被加了同类型的锁

  • 多粒度锁上的两种不同加锁方式

    • 显式加锁:应事务的请求直接加到数据对象上的锁
    • 隐式加锁:本身没有被显式加锁,但因为其上层结点加了锁而使数据对象被加锁
  • 给一个结点显式加锁时必须考虑

    • 该结点是否已有不相容的显式锁存在
    • 该结点是否已有不相容的隐式锁存在
    • 所有下层结点中是否存在不相容的显式锁

    理论上要搜索上面全部的可能情况,才能确定P上的锁请求能否成功,显然是低效的,因此引入意向锁

3.意向锁

  1. IS锁 Intent Share Lock,意向共享锁,意向读锁
  2. IX锁 Intent Exclusive Lock,意向排他锁,意向写锁
  3. 如果对某个结点加IS(IX)锁,则说明事务要对该结点的某个下层结点加S(X)锁
  4. 对任一结点P加S(X)锁,必须先对从根结点到P的路径上的所有结点加IS(IX)锁

四、事务的隔离级别

  1. 隔离级别是针对连接(会话)而设置的,不是针对一个事务
  2. 不同隔离级别影响读操作。
隔离级别脏读不可重复读取幻象
未提交读
提交读
可重复读
可串行读

1. 未提交读 Read Uncommitted

  1. 允许读取当前页面上的任何数据,不管是否已经提交。
  2. 事务不必等待任何锁,也不需要对读取的数据加锁。
  3. 实际DBMS中一般不使用。

2. 提交读 Read Committed

  1. 保证事务不会读取到其他未提交事务所修改的数据(可防止脏读)
  2. 事务必须在所访问数据上加S锁,数据一旦读出,就马上释放持有的S锁

3. 可重复读 Repeatable Read

  1. 保证事务在事务内部如果重复访问同一数据(记录集),数据不会发生改变。即,事务在访问数据时,其他事务不能修改正在访问的那部分数据
  2. 可重复读可以防止脏读和不可重复读取,但不能防止幻像
  3. 事务必须在所访问数据上加S锁,防止其他事务修改数据,而且S锁必须保持到事务结束

4. 可串行读 Serializable Read

  1. 保证事务调度是可串化的
  2. 事务在访问数据时,其他事务不能修改数据,也不能插入新元组
  3. 事务必须在所访问数据上加S锁,防止其他事务修改数据,而且S锁必须保持到事务结束
  4. 事务还必须锁住访问的整个表

死锁

死锁的两种处理策略

  • 死锁检测:检测到死锁,再解锁
  • 死锁预防:提前采取措施防止出现死锁

死锁检测

  • 超时
  • 等待图
    • 结点:事务
    • 边:Ti->Tj,Ti必须等待Tj释放所持有的某个锁才能继续执行。
    • 如果等待图中出现了环路,说明产生了死锁。

死锁预防

1. 按封锁对象的某种优先顺序加锁

  • 把要加锁的数据库元素按照某种顺序排序
  • 事务只能按照元素顺序申请锁

2. 使用时间戳

  • 每个事务开始时赋予一个时间戳
  • 如果事务T被Rollback然后再Restart,T的时间戳不变
  • Ti请求被Tj持有的锁,根据Ti和Tj的timestamp决定锁的授予
1.等待-死亡

T请求一个被U持有的锁

  • 如果T的时间戳小于U,那么T需要等待
  • 如果T的时间戳大于U,那么T需要rollback,之后再restart,时间戳和之前相同
2. 伤害-等待

T请求一个被U持有的锁

  • 如果T的时间戳小于U,U释放锁,并且rollback和restart
  • 如果T的时间戳大于U,那么T需要等待

两种并发控制思路

1. 悲观并发控制

  • 立足于事先预防事务冲突
  • 采用锁机制实现,事务访问数据前都要申请锁
  • 锁机制影响性能,容易带来死锁、活锁等副作用

操作之前先加锁,操作完释放锁,无法避免死锁。适合写频繁的应用场景

2. 乐观并发控制

  • 乐观并发控制假定不太可能(但不是不可能)在多个用户间发生资
    源冲突,允许不锁定任何资源而执行事务。
  • 只有试图更改数据时才检查资源以确定是否发生冲突。如果发生冲突,应用程序必须读取数据并再次尝试进行更改。
  • 读阶段—>有效性确认阶段—>写阶段

操作之前不加锁,只有写提交时才检查是否发生了写冲突。适合读频繁的应用场景

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值