1 --死锁问题定位与解决方法 2 3 --为了解决死锁问题,SQLSERVER数据库引擎死锁监视器会定期检查陷入死锁的任务 4 --如果监视器检测到这种依赖循环关系,会选择其中一个任务作为牺牲品,然后 5 --终止其事务并提示错误。这就是用户会遇到的死锁错误 6 7 --可以发生死锁的资源 8 --需要说明的是,死锁不是只发生在锁资源上,以下类型的资源都可能会造成阻塞,并 9 --最终导致死锁 10 11 --1、锁:例如:页,行,元数据和应用程序上的锁 12 --2、工作线程:如果排队等待线程的任务拥有阻塞其他工作线程的资源,也会导致死锁 13 --例如:会话1Session1启动事务并获取行1ROW1的共享锁(S锁)后,进入睡眠状态。 14 --在所有可用工作线程上运行的活动会话正尝试获取行ROW1的排他锁(X锁)。 15 --当会话Session1苏醒后,因为无法获取工作线程,所以无法提交事务并释放 16 --行ROW1的锁,导致死锁 17 18 --3、内存:当并发请求等待获得内存,而当前的可用内存无法满足其需要时,可能 19 --发生死锁。例如:两个并发查询(Q1和Q2)作为用户定义函数执行,分别获取 20 --10MB和20MB内存。如果每个查询需要30MB而可用总内存为20MB,则Q1和Q2必须 21 --等待对方释放内存,导致死锁 22 23 --4、并行查询执行的相关资源:当一条语句用多个线程运行时,线程和线程之间可能 24 --会发生死锁 25 26 --死锁检测 27 --上面列出的死锁SQL都能用他的死锁检测机制检测出来。死锁检测是由锁监视器 28 --这个系统线程执行的,他会定期搜索SQL里的所有任务。默认时间间隔是5秒 29 30 --检测到死锁后,SQL会选择其中一个线程作为死锁牺牲品来结束死锁。数据库引擎 31 --终止线程当前执行的批处理,回滚死锁牺牲品的事务,并将1205错误返回到 32 --应用程序。回滚死锁牺牲品会释放事务持有的所有锁。这使其他线程的事务解锁 33 --并继续运行。默认情况下,数据库引擎会选择运行回滚开销最小的事务的会话 34 --作为死锁牺牲品 35 36 37 38 --死锁与阻塞的差别 39 --很多用户会把死锁和阻塞混淆起来。当一个事务请求被其他事务锁定的资源上的锁时, 40 --发出请求的事务会一直等待,直到该锁被别人释放,自己申请到为止。这时发生的 41 --是阻塞。默认情况下,除非设置了SET LOCK_TIMEOUT 30 30为超时时间值 42 --否则SQL事务会一直等下去,直到他自己超时为止。所以阻塞往往会持续很长时间 43 --对称性的并发性能影响很大。有些人称这种现象为“锁死”,因为这种等待会一直 44 --持续,SQL不会做出干预 45 46 47 48 --而死锁虽然是两个或多个进程之间的相互等待,但是由于SQL有数据库引擎死锁检测 49 --方案,至少5秒钟会消除掉一个现有的死锁,所以对最终用户带来的影响是某些 50 --指令请求失败,对性能影响往往没有像阻塞那么严重 51 52 53 --问题定位 54 --为了查看死锁信息,SQL提供了监视工具:两个跟踪标志:1204和1222 55 --以及SQL Trace 56 57 --跟踪标志1204和跟踪标志1222 58 --发生死锁时,跟踪标志1204和1222会向SQL错误日志返回捕获的信息 59 --跟踪标志1204会报告由死锁所涉及的每个节点设置格式的死锁信息 60 --他是SQL2005之前版本就有的功能。跟踪标志1222是SQL2005才开始 61 --有的新功能,会设置死锁信息的格式,顺序为先按进程,然后按 62 --资源。1222的结果不但基本包含了1204的所有信息,还包含许多1204 63 --所没有的信息。所以在SQL2005以后可以直接使用跟踪标志1222来 64 --跟踪死锁 65 66 67 --打开跟踪标志 68 DBCC TRACEON(1222,-1) 69 70 --和1204输出结果不同,1222的输出结果分成3个部分,并且由于信息量比较多,长度要比 71 --1204的结果长很多.死锁的参与者不是用SPID来直接显示,而是用processXXXX的方法 72 --所以需要有些耐心来看懂。但是,包含的内容比1204的输出丰富很多 73 74 75 --1222包含3部分 76 --第一部分: 77 --死锁牺牲进程 78 79 --第二部分:process-list 80 --死锁发生的进程信息 81 82 --第三部分:resource-list 83 --发生死锁的资源信息 84 85 86 87 --死锁图形事件(locks-deadlock graph) 88 --在SQL Trace中也有表示死锁所涉及的任务和资源的图形描述的事件 89 90 --要分析死锁的时候,一般结合跟踪标志1222和SQL Trace。首先在sql errorlog 91 --里寻找跟踪标志1222的输出结果,根据输出的时间在跟踪里寻找相应的连接 92 --接着就可以详细分析死锁产生的原因了 93 94 95 ----------------------------解决办法--------------------------------------------------------------------- 96 --尽管死锁不能完全避免,但遵守特定的编码惯例可以将死锁发生的机会将至最低。 97 --以下方法有助于将死锁减至最少: 98 --按同一顺序访问对象 99 --避免事务中的用户交互 100 --保持事务简短并处于一个批处理中 101 --使用较低的隔离级别 102 --调整语句的执行计划,减少锁的申请数目 103 104 105 106 107 --按同一顺序访问对象 108 --如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果 109 --两个并发事务先获取supplier(生产者)表上的锁,然后获取 consumer(消费者)表 110 --上的锁。则在其中一个事务完成之前,另一个事务将在supplier表上被阻塞 111 --当第一个事务提交或回滚之时,第二个事务将继续执行,这样就不会发生死锁 112 113 114 --避免事务中的用户交互 115 --避免编写包含用户交互的事务,因为没有用户干预的批处理的运行速度 116 --远快于用户必须手动响应查询时的速度。例如,如果事务正在等待用户 117 --输入,而用户去吃午餐甚至回家过周未,那用户就耽误了事务的完成 118 --这将降低系统的吞吐量,事务持有的任何锁只有在事务提交或回滚后 119 --才能释放。即使不出现死锁的情况,在占用资源的事务完成之前,访问 120 --同一资源的其他事务也会被阻塞 121 122 123 --保持事务简短并处于一个批处理中 124 --在同一数据库中并发执行多个需要长时间运行的事务会更容易发生死锁。 125 --事务的运行时间越长,他持有排他锁或更新锁的时间就越长,从而会 126 --阻塞其他活动并可能导致死锁 127 --保持事务处于一个批处理中可以最小化事务中的网络通信往返量,减少 128 --完成事务和释放锁可能遭遇的延迟 129 130 131 --使用较低的隔离级别 132 --确定事务是否能在较低的隔离级别上运行。使用较低的隔离级别(例如:已提交读) 133 --比使用较高的隔离级别(例如:可序列化)持有共享锁的时间更短。这样就 134 --减少了锁争用,可帮助避免阻塞和死锁 135 136 137 --调整语句的执行计划,减少锁的申请数目 138 --和阻塞类似,死锁产生的一大原因也是做一件事情需要申请比较多的资源。 139 --例如SQL需要扫描整张表才能找到修改的记录,而在扫描的过程中,SQL要 140 --为读到的每一条记录加锁。如果执行计划是seek,需要读的记录数目比较 141 --少,申请的锁数目也会比较少,可能就能避免死锁 142 143 144 145 --从上面的建议看,除了按同一顺序访问对象这个要求外,避免阻塞和死锁很多 146 --地方是相通的,所以采用的手段也很相似。唯一不同的是,有时候在高并发 147 --的情况下,可能死锁是很难避免的。这时候解决问题的手段之一,可以是 148 --提高连接申请的锁粒度,让死锁的一方先因为另一方已获取了一个粒度很高 149 --的锁(例如:页面锁甚至表锁)而先遇到阻塞。以暂时的阻塞代替死锁, 150 --使得双方能依次完成,虽然速度稍慢了一些
时间: 2024-10-12 02:39:56