• 数据库中死锁那些事儿


    转自:http://blog.csdn.net/eseaqyq/article/details/7795023

    ————————————————————————————————————————————————————

    说起数据中的死锁,已经多次在笔试题目中遇到。今天特此做一个数据库死锁方面的总结,以绝后患,吼吼!

    一、首先我们来看几个定义:

     

    1.死锁

    所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。
     

    2.数据库

    数据库(Database)是按照数据结构来组织、存储和管理数据的仓库。
     

    3.产生死锁的必要条件

    •  互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
    • 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己获得的其它资源保持不放。
    • 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
    • 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

    4.处理死锁的基本方式

          1) 预防死锁。
     
      这是一种较简单和直观的事先预防的方法。方法是通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。预防死锁是一种较易实现的方法,已被广泛使用。但是由于所施加的限制条件往往太严格,可能会导致系统资源利用率和系统吞吐量降低。
     
      2) 避免死锁。
     
      该方法同样是属于事先预防的策略,但它并不须事先采取各种限制措施去破坏产生死锁的的四个必要条件,而是在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。
     
      3)检测死锁。
     
      这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区,此方法允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源,然后采取适当措施,从系统中将已发生的死锁清除掉。
     
      4)解除死锁。
     
      这是与检测死锁相配套的一种措施。当检测到系统中已发生死锁时,须将进程从死锁状态中解脱出来。常用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。死锁的检测和解除措施,有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。
     

    二、数据库中的死锁是什么样的!

     

    1.SQL server锁的机制

    SQL server的所有活动都会产生锁。锁定的单元越小,就越能越能提高并发处理能力,但是管理锁的开销越大。如何找到平衡点,使并发性和性能都可接受是SQL Server的难点。
    SQL Server有如下几种琐:
    1、 共享锁
    用于只读操作(SELECT),锁定共享的资源。共享锁不会阻止其他用户读,但是阻止其他的用户写和修改。
    2、 更新锁
    更新锁是一种意图锁,当一个事物已经请求共享琐后并试图请求一个独占锁的时候发生更新琐。例如当两个事物在几行数据行上都使用了共享锁,并同时试图获取独占锁以执行更新操作时,就发生了死锁:都在等待对方释放共享锁而实现独占锁。更新锁的目的是只让一个事物获得更新锁,防止这种情况的发生。
    3、 独占锁(排它锁)
    一次只能有一个独占锁用在一个资源上,并且阻止其他所有的锁包括共享缩。写是独占锁,可以有效的防止’脏读’
    4、 意图锁
    在使用共享锁和独占锁之前,使用意图锁。从表的层次上查看意图锁,以判断事物能否获得共享锁和独占锁,提高了系统的性能,不需从爷或者行上检查。
    5、 计划锁
    Sch-M,Sch-S。对数据库结构改变时用Sch-M,对查询进行编译时用Sch-S。这两种锁不会阻塞任何事物锁,包括独占锁。

    读是共享锁,写是排他锁,先读后更新的操作是更新锁,更新锁成功并且改变了数据时更新锁升级到排他锁。

    2.下面我们看下如何避免死锁

     
    1 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务;
    2 设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂;
    3 所有的SP都要有错误处理(通过@error)
    4 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁
    5 优化程序,检查并避免死锁现象出现;
    1)合理安排表访问顺序
    2)在事务中尽量避免用户干预,尽量使一个事务处理的任务少些。
    3)采用脏读技术。脏读由于不对被访问的表加锁,而避免了锁冲突。在客户机/服务器应用环境中,有些事务往往不允许读脏数据,但在特定的条件下,我们可以用脏读。
    4)数据访问时域离散法。数据访问时域离散法是指在客户机/服务器结构中,采取各种控制手段控制对数据库或数据库中的对象访问时间段。主要通过以下方式实现: 合理安排后台事务的执行时间,采用工作流对后台事务进行统一管理。工作流在管理任务时,一方面限制同一类任务的线程数(往往限制为1个),防止资源过多占用; 另一方面合理安排不同任务执行时序、时间,尽量避免多个后台任务同时执行,另外,避免在前台交易高峰时间运行后台任务
    5)数据存储空间离散法。数据存储空间离散法是指采取各种手段,将逻辑上在一个表中的数据分散到若干离散的空间上去,以便改善对表的访问性能。主要通过以下方法实现: 第一,将大表按行或列分解为若干小表; 第二,按不同的用户群分解。
    6)使用尽可能低的隔离性级别。隔离性级别是指为保证数据库数据的完整性和一致性而使多用户事务隔离的程度,SQL92定义了4种隔离性级别:未提交读、提交读、可重复读和可串行。如果选择过高的隔离性级别,如可串行,虽然系统可以因实现更好隔离性而更大程度上保证数据的完整性和一致性,但各事务间冲突而死锁的机会大大增加,大大影响了系统性能。
    7)使用Bound Connections。Bound connections 允许两个或多个事务连接共享事务和锁,而且任何一个事务连接要申请锁如同另外一个事务要申请锁一样,因此可以允许这些事务共享数据而不会有加锁的冲突。
    8)考虑使用乐观锁定或使事务首先获得一个独占锁定。 
    大家看下下面的情况是否会出现死锁呢?
     
    A. select , update,select
    B.select,select,update
    C.select,update,update
    D.update,select,update
    上面这四种情况哪种会出现死锁,哪种出现死锁呢?你的理由又是什么呢?
     

    3.下面我们来看下下面的这个死锁问题!

    1. <span style="font-family:'Microsoft YaHei';">-- A事务先更新table1表,然后延时30秒,再更新table2表;  
    2. begin tran  
    3. update table1 set A='aa' where B='b2';  
    4. --这将在 Table1 中生成排他行锁,直到事务完成后才会释放该锁。  
    5. waitfor delay '00:00:30';  
    6. --进入延时  
    7. update table2 set D='d5' where E='e1' ;  
    8. commit tran  
    9. -- B事务先更新table2表,然后延时10秒,再更新table1表;  
    10. begin tran  
    11. update table2 set D='d5' where E='e1';  
    12. --这将在 Table2 中生成排他行锁,直到事务完成后才会释放该锁  
    13. waitfor delay '00:00:10'  
    14. --进入延时  
    15. update table1 set A='aa' where B='b2' ;  
    16. commit tran</span>  
     
    A事务先更新table1表,然后延时30秒,再更新table2表;begin tranupdate table1 set A='aa' where B='b2';--这将在 Table1 中生成排他行锁,直到事务完成后才会释放该锁。waitfor delay '00:00:30';--进入延时update table2 set D='d5' where E='e1' ;commit tran-- B事务先更新table2表,然后延时10秒,再更新table1表;begin tranupdate table2 set D='d5' where E='e1';--这将在 Table2 中生成排他行锁,直到事务完成后才会释放该锁waitfor delay '00:00:10'--进入延时update table1 set A='aa' where B='b2' ;commit tran
    若并发执行上述两个事务,A,B两事务都要等待对方释放排他锁,这样便形成了死锁。
  • 相关阅读:
    第十八周作业
    第十七周作业
    第十六周作业
    第十五周作业
    第十四周作业
    第十三周作业
    第十二周作业
    第二阶段考试
    第十周作业
    启航,带着梦想出发!
  • 原文地址:https://www.cnblogs.com/kaikailele/p/3990415.html
Copyright © 2020-2023  润新知