下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

数据库死锁分析与解决方案

作者:课课家教育the     来源: http://www.kokojia.com点击数:974发布时间: 2017-06-02 13:00:10

标签: 数据库Oracle数据库死锁

  所谓死锁,你知道是什么吗?

  死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。一种情形,此时执行程序中两个或多个线程发生永久堵塞(等待),每个线程都在等待被其他线程占用并堵塞了的资源。例如,如果线程A锁住了记录1并等待记录2,而线程B锁住了记录2并等待记录1,这样两个线程就发生了死锁现象。计算机系统中,如果系统的资源分配策略不当,更常见的可能是程序员写的程序有错误等,则会导致进程因竞争资源不当而产生死锁的现象。锁有多种实现方式,比如意向锁,共享-排他锁,锁表,树形协议,时间戳协议等等。锁还有多种粒度,比如可以在表上加锁,也可以在记录上加锁。

  一、死锁的表现

  1、错误信息是:事务(进程ID)与另一个进程被死锁在锁资源上,并且已被选作死锁牺牲品。请重新运行该事务。

  2、错误信息是:事务(进程ID)与另一个进程被死锁在锁|通信缓冲区资源上,并且已被选作死锁牺牲品。请重新运行该事务。

  二、死锁的原因

  1、系统资源不足,进程运行推进的顺序不合适。由于多用户、多任务的并发性和事务的完整性要求,当多个事务处理对多个资源同时访问时,若双方已锁定一部分资源但也都需要对方已锁定的资源时,无法在有限的时间内完全获得所需的资源,就会处于无限的等待状态,从而造成其对资源需求的死锁。

  2、数据库本身加锁机制的实现方法不同,各数据库系统也会产生其特殊的死锁情况。如在SybaseSQLServer11中,最小锁为2K

  一页的加锁方法,而非行级锁。如果某张表的记录数少且记录的长度较短(即记录密度高,如应用系统中的系统配置表或系统参数表就属于此类表),被访问的频率高,就容易在该页上产生死锁。

  三、产生死锁的四个必要条件

  (1)互斥条件:一个资源每次只能被一个进程使用。

  (2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

  (3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

  (4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

  这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

  表现一:

  一个用户A访问表A(锁住了表A),然后又访问表B。另一个用户B访问表B(锁住了表B),

  然后企图访问表A。这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,

  才能继续,同样用户B要等用户A释放表A才能继续操作,这样就造成了死锁。

  解决方法:

  这种死锁是由于程序的BUG产生的,需调整程序对数据库层的实现逻辑。仔细分析程序的逻辑:

  (1)尽量避免同时锁定两个资源。

  (2)必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源。

  表现二:

  用户A读一条纪录,然后修改该条纪录,同时用户B也修改该条纪录。这里用户A的事务里锁的性质由共享锁企图上升到独占锁(forupdate),而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释

  放共享锁,于是出现了死锁。这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。

  解决方法:

  让用户A的事务(即先读后写类型的操作),在select时就是用updatelock。

  语法如下:select*fromtable1with(updlock)where….

  四、死锁的预防和解除

  理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源,在系统运行过程中,对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,若分配后系统可能发生死锁,则不予分配,否则予以分配。因此,对资源的分配要给予合理的规划。

  五、常见死锁情况及解决方法

  1、不同的存储过程、触发器、动态SQL语句段按照不同的顺序同时访问多张表。

  在系统实现时应规定所有存储过程、触发器、动态SQL语句段中,对多张表的操作总是使用同一顺序。如:有两个存储过程procedure1、procedure2,都需要访问三张表table1、table2

  和table3,如果procedure1按照table1、table2和table3的顺序进行访问,那么,procedure2也应该按照以上顺序访问这3张表。

  2、在交换期间添加记录频繁的表,但在该表上使用了非群集索引(non-clustered)。

  对在交换期间添加记录频繁的表,使用群集索引(clustered),以减少多个用户添加记录到该表的最后一页上,在表尾产生热点,造成死锁。这类表多为往来账的流水表,其特点是在交换期间需要在表尾追加大量的记录,并且对已添加的记录不做或较少做删除操作。

  3、表中的记录少,且单条记录较短,被访问的频率较高。

  对单张表中记录数不太多,且在交换期间select或update较频繁的表可使用设置每页最大行的办法,减少数据在表中存放的密度,模拟行级锁,减少在该表上死锁情况的发生。这类表多为信息繁杂且记录条数少的表。

  如:系统配置表或系统参数表。在定义该表时添加如下语句:withmax_rows_per_page=1

  4、整张表被访问的频率高(如代码对照表的查询等)。

  在存储过程、触发器、动态SQL语句段中,若对某些整张表select操作较频繁,则可能在

  该表上与其他访问该表的用户产生死锁。对于检查账号是否存在,但被检查的字段在检查期间不会被更新等非关键语句,可以采用在select命令中使用atisolationreaduncommitted子句的方法解决。该方法实际上降低了select语句对整张表的锁级别,提高了其他用户对该表

  操作的并发性。在系统高负荷运行时,该方法的效果尤为显著。

  例如:select*fromtitlesatisolationreaduncommitted

  5、对流水号一类的顺序数生成器字段,可以先执行update流水号字段+1,然后再执行select

  获取流水号的方法进行操作。

  备注:

  NOLOCK(不加锁):此选项被选中时,SQLServer在读取或修改数据时不加任何锁。在这种情况下,用户有可能读取到未完成事务(UncommittedTransaction)或回滚(RollBack)中数据,即所谓的“脏数据”。

  UPDLOCK(修改锁):此选项被选中时,SQLServer在读取数据时使用修改锁来代替共享锁,并将此锁保持至整个事务或命令结束。使用此选项能够保证多个进程能同时读取数据但只有该进程能修改数据。

    六、死锁监控与查看

  1、可通过服务器操作系统自带的“性能”工具或LoadRunner等第3方性能测试工具,添加

  性能计数器NumberofDeadlocks/sec(SQLServer|Locks_Total)进行监控。

性能计数器NumberofDeadlocks/sec(SQLServer|Locks_Total)进行监控

  4.1系统自带的“性能”工具监控图

 备注:    NOLOCK(不加锁):此选项被选中时,SQLServer在读取或修改数据时不加任何锁。在这种情况下,用户有可能读取到未完成事务(UncommittedTransaction)或回滚(RollBack)中数据,即所谓的“脏数据”。    UPDLOCK(修改锁):此选项被选中时,SQLServer在读取数据时使用修改锁来代替共享锁,并将此锁保持至整个事务或命令结束。使用此选项能够保证多个进程能同时读取数据但只有该进程能修改数据。      六、死锁监控与查看    1、可通过服务器操作系统自带的“性能”工具或LoadRunner等第3方性能测试工具,添加    性能计数器NumberofDeadlocks/sec(SQLServer|Locks_Total)进行监控。

  4.2LoadRunner性能测试工具监控图

  2、发生死锁后可通过如下存储过程查看详细的死锁信息,如下所示:

  //死锁详细信息打印.txt

 4.2LoadRunner性能测试工具监控图    2、发生死锁后可通过如下存储过程查看详细的死锁信息,如下所示:    //死锁详细信息打印.txt

  //数据库里阻塞和死锁情况查看.sql

//数据库里阻塞和死锁情况查看.sql

//数据库里阻塞和死锁情况查看.sql

  七、死锁现象重现与模拟

  1、驱动数据构造

  一般对于事务性死锁,均产生在事务性操作(同时对同一表进行读和写操作)中。所以,首

  先需构造驱动数据,满足相应操作条件(where或if中判断条件),模拟对同一张表的读和

  写操作。

  2、并发性模拟

  由于死锁产生在对表操作的同一时刻,所以可以借助第三方支持并发测试的工具来实现(例

  如:LoadRunner)。当然也可以采用单元测试的方式实现,即在数据库层调用同一存储过程,

  然后使用无限循环调用的方式进行模拟。

  八、如何将死锁减至最少?

  虽然不能完全避免死锁,但可以使死锁的数量减至最少。将死锁减至最少可以增加事务的吞吐量并减少系统开销,因为只有很少的事务回滚,而回滚会取消事务执行的所有工作。由于死锁时回滚而由应用程序重新提交。

  下列方法有助于最大限度地降低死锁:

  (1)按同一顺序访问对象。

  (2)避免事务中的用户交互。

  (3)保持事务简短并在一个批处理中。

  (4)使用低隔离级别。

  (5)使用绑定连接。

  九、关于死锁的预防建议

  1、优化索引

  2、对所有的报表,非事务性的select语句在from后都加了with(nolock)语句

  3、对所有的事务性更新尽量使用相同的更新顺序来执行

  小编结语:

  更多内容尽在课课家教育~~

  

赞(25)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程