zoukankan      html  css  js  c++  java
  • 第11章:并发控制

    第11章:并发控制

    代码是基于SQLServer学习,与MySQL有略微差别!

    11.1、并发控制概述

    多用户数据库系统

    • 允许多个用户同时使用的数据库系统称为多用户数据库系统。
    • 多用户数据库系统面临着执行多事务的情况。

    事务并发执行带来的问题

    • 并发执行的多个事务可能会同时存取同一数据,造成数据的不一致,破坏事务的隔离性和数据库的一致性。
    • DBMS必须提供并发控制机制解决事务并发执行带来的问题。(并发控制器)
    • 并发控制机制是衡量一个DBMS性能的重要标志之一。

    并发操作带来的数据不一致性:

    1、丢失修改(lost update)
    2、不可重复读(non-repeatable read)
    3、读“脏”数据(dirty read)

    丢失修改

    ​ 丢失修改是指事务T1与事务T2从数据库中读入同一数据并修改,事务T2的提交结果破坏了事务T1提交的结果,导致事务T1的修改被丢失。

    在这里插入图片描述

    CREATE TABLE sales
    (
    	id char(2),
    	qty int
    )
    insert into sales values( 'A1', 10 )	
    
    -- 事务一
    begin tran
    	declare @sl int
    	select @sl = qty from sales where id = 'A1'
    	waitfor delay '00:00:15.000'
    	update sales set qty = @sl - 1 where id = 'A1'
    commit tran
    -- 事务二
    begin tran
    	declare @sl int
    	select @sl = qty from sales where id = 'A1'
    	waitfor delay '00:00:15.000'
    	update sales set qty = @sl - 1 where id = 'A1'
    commit tran
    

    不可重复度

    1、事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时,得到与前一次不同的值。

    在这里插入图片描述

    begin tran
    		declare @sl int
    		declare @s2 int
    		select @sl = qty from sales where id = 'A1'
    		print(@sl)
    		waitfor delay '00:00:30.000'
    		select @s2 = qty from sales where id = 'A1'
    		print(@s2)
    commit tran
    begin tran
    		update sales set qty = qty *2 where id = 'A1'
    commit tran
    

    2、事务T1按一定条件读取某些数据记录后,事务T2删除了其中部分记录,当事务T1再次按相同条件读取数据时,发现某些记录神密地消失了。
    3、事务T1按一定条件读取某些数据记录后,事务T2插入了一些记录,当事务T1再次按相同条件读取数据时,发现多了一些记录。

    后两种不可重复读有时也称为幻影现象

    读“脏”数据

    ​ 事务T1修改某一数据,并将其写回磁盘,事务T2读取同一数据后,事务T1由于某种原因被撤消,这时事务T1已修改过的数据恢复原值,事务T2读到的数据就与数据库中的数据不一致,是不正确的数据,又称为“脏”数据。

    在这里插入图片描述

    事务并发执行会导致数据不一致性

    • 由于并发操作破坏了事务的隔离性

    并发控制就是要用正确的方式调度并发操作,使一个事务的执行不受其他事务的干扰,从而避免造成数据的不一致性

    并发控制的主要技术

    • 封锁(Locking)
    • 时间戳(Timestamp)
    • 乐观控制法

    商用的DBMS一般都采用封锁方法

    11.2、封锁(Locking)

    11.2.1、封锁概述

    什么是封锁?

    • 封锁就是事务T在对某个数据对象(例如表、记录等)操作之前,先向系统发出请求,对其加锁。
    • 如果事务T的加锁请求得到满足,事务T就对该数据对象有了一定的控制。
    • 如果事务T的加锁请求得不到满足,事务T就不能控制该数据对象。
    • 封锁是实现并发控制的一个非常重要的技术

    基本封锁类型

    • 排它锁(Exclusive lock,简记为X锁)
    • 共享锁(Share lock,简记为S锁)

    排它锁(Exclusive lock,简记为X锁)

    • 排它锁又称为写锁。
    • 若事务T对数据对象A加上X锁,允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。

    共享锁(Share lock,简记为S锁)

    • 共享锁又称为读锁。
    • 若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其它事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。

    在这里插入图片描述
    封锁协议(Locking Protocol)

    • 在运用X锁和S锁对数据对象加锁时,需要约定一些规则(封锁协议):
      • 何时申请X锁或S锁
      • 何时释放X锁或S锁
    • 常用的封锁协议:三级封锁协议。

    11.2.2、一级封锁协议

    • 事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。
    • 一级封锁协议可防止丢失修改
    • 在一级封锁协议中,如果是读数据,是不需要加锁的,所以它不能避免不可重复读和读“脏”数据。
    • 在这里插入图片描述在这里插入图片描述
      在这里插入图片描述

    11.2.3、二级封锁协议

    • 一级封锁协议+事务T在读取数据R前必须先加S锁,读完后不等事务结束即可释放S锁。
    • 二级封锁协议可以防止丢失修改和读“脏”数据。
    • 在二级封锁协议中,由于读完数据后即可释放S锁,所以它不能避免不可重复读。
      在这里插入图片描述
      在这里插入图片描述

    11.2.4、三级封锁协议

    • 一级封锁协议 + 事务T在读取数据R之前必须先对其加S锁,直到事务结束才释放
    • 三级封锁协议可避免丢失修改、读“脏”数据和不可重复读。

    在这里插入图片描述
    在这里插入图片描述

    11.2.5、SQL Server的并发机制

    测试加排它锁(X锁)能否避免丢失修改:

    CREATE TABLE sales
    (
    	id char(2),
    	qty int
    )
    insert into sales values( 'A1', 10 )	
    -- 【事务一】
    begin tran
    	declare @sl int
    	select @sl = qty from sales with(Xlock)
    							where id = 'A1'
    	waitfor delay '00:00:10.000'
    	update sales set qty = @sl - 1 where id = 'A1'
    commit tran
    
    -- 【事务二】
    begin tran
    	declare @sl int	
    	select @sl=qty from sales with(Xlock)
    							where id='A1'
    	waitfor delay '00:00:10.000'
    	update sales set qty = @sl - 1 where id = 'A1'
    commit tran
    
    • 事务隔离
      • 对于编程人员来说,不用手工去设置控制锁,通过设置事务的隔离级别自动管理锁。

    在这里插入图片描述

    -- 【设置事务的隔离级别】
    set transaction isolation level
    -- 【事务隔离级别】
    read uncommitted
    read committed
    repeatable read
    serializable
    

    测试事务隔离级别为可重复读:

    -- 设置为可重复度
    set transaction isolation level repeatable read
    -- 【事务一】
    begin tran
    		declare @sl int
    		declare @s2 int
    		select @sl = qty from sales where id = 'a1'
    		print(@sl)
    		waitfor delay '00:00:30.000'
    		select @s2 = qty from sales where id = 'a1'
    		print(@s2)
    commit tran
    -- 【事务二】
    begin tran
    		update sales set qty = qty *2 where id = 'a1'
    commit tran
    

    11.3、活锁和死锁

    活锁

    • 事务T1封锁了数据R
    • 事务T2又请求封锁R,于是T2等待。
    • T3也请求封锁R,当T1释放了R上的封锁之后系统首先批准了T3的请求,T2仍然等待。
    • T4又请求封锁R,当T3释放了R上的封锁之后系统又批准了T4的请求……
    • T2有可能永远等待(饿死)。

    如何避免活锁

    • 采用先来先服务的策略:
    • 当多个事务请求封锁同一数据对象时,按请求封锁的先后次序对这些事务排队,该数据对象上的锁一旦释放,首先批准申请队列中第一个事务获得锁。

    死锁

    在这里插入图片描述
    解决死锁的方法:

    1. 采取一定措施预防死锁的发生。
    2. 允许死锁发生,采取一定方法诊断死锁、解除死锁。

    死锁的预防

    • 一次封锁法
      • 要求每个事务必须一次将所有要使用的数据全部加锁,否则就不能继续执行
    • 一次封锁法存在的问题:
      • 以后要用到的全部数据加锁,势必扩大了封锁的范围,从而降低了系统的并发度。

    在这里插入图片描述

    • 顺序封锁法:

      • 预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实行封锁。
    • 顺序封锁法存在的问题

      • 数据库系统中可封锁的数据对象极其众多,而且还在不断变化,要维护这样极多而且变化的资源的封锁顺序非常困难,成本很高。
      • 事务的封锁请求可以随着事务的执行而动态地决定,很难事先确定每一个事务要封锁哪些对象,因此也就很难按规定的顺序去施加封锁。

    在这里插入图片描述

    死锁的诊断

    • 超时法:
      • 如果一个事务的等待时间超过了规定的时限,就认为发生了死锁
      • 优点:实现简单
      • 缺点
        • 有可能误判死锁。
        • 时限若设置得太长,死锁发生后不能及时发现。

    死锁的诊断

    • 等待图法:
      • 用事务等待图动态反映所有事务的等待情况
      • 事务等待图是一个有向图G=(T,U)

    在这里插入图片描述

    • 并发控制子系统周期性地(比如每隔1min)检测事务等待图,如果发现图中存在回路,则表示系统中出现了死锁。

    死锁的解除

    选择一个处理死锁代价最小的事务,将其撤消,释放此事务持有的所有的锁,使其它事务能继续运行下去。

  • 相关阅读:
    天平天国和诗,和远方,和苟且
    ui设计师做好这几点,产品经理准闭嘴
    谈谈个人视觉创意的学习经验
    保持觉知
    第37周本人进步点
    扒一扒万恶的导航网站
    16年第四本《简单的逻辑学》
    hey,开通个人公众号啦!
    今朝有酒今朝醉,也是一种理性-附个人心得
    元素的定位
  • 原文地址:https://www.cnblogs.com/rainszj/p/12989020.html
Copyright © 2011-2022 走看看