死锁,简而言之,两个或者多个trans,同时请求对方正在请求的某个对象,导致双方互相等待。简单的例子如下:
trans1 trans2
------------------------------------------------------------------------
1IDBConnectionBeginTransaction 1IDBConnectionBeginTransaction
2update table A 2update table B
3update table B 3update table A
4IDBConnectionCommit 4IDBConnectionCommit
那么,很容易看到,如果trans1和trans2,分别到达了step3,那么trans1会请求对于B的X锁,trans2会请求对于A的X锁,而二者的锁在step2上已经被对方分别持有了。由于得不到锁,后面的Commit无法执行,这样双方开始死锁。
好,我们看一个简单的例子,来解释一下,应该如何解决死锁问题。
-- Batch #1
CREATE DATABASE deadlocktest
GO
USE deadlocktest
SET NOCOUNT ON
DBCC TRACEON (1222, -1)
-- 在SQL2005中,增加了一个新的dbcc参数,就是1222,原来在2000下,我们知道,可以执行dbcc
--traceon(1204,3605,-1)看到所有的死锁信息。SqlServer 2005中,对于1204进行了增强,这就是1222。
GO
IF OBJECT_ID ('t1') IS NOT NULL DROP TABLE t1
IF OBJECT_ID ('p1') IS NOT NULL DROP PROC p1
IF OBJECT_ID ('p2') IS NOT NULL DROP PROC p2
GO
CREATE TABLE t1 (c1 int, c2 int, c3 int, c4 char(5000))
GO
DECLARE @x int
SET @x = 1
WHILE (@x <= 1000) BEGIN
INSERT INTO t1 VALUES (@x2, @x2, @x2, @x2)
SET @x = @x + 1
END
GO
CREATE CLUSTERED INDEX cidx ON t1 (c1)
CREATE NONCLUSTERED INDEX idx1 ON t1 (c2)
GO
CREATE PROC p1 @p1 int AS SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
GO
CREATE PROC p2 @p1 int AS
UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1
UPDATE t1 SET c2 = c2-1 WHERE c1 = @p1
GO
上述sql创建一个deadlock的示范数据库,插入了1000条数据,并在表t1上建立了c1列的聚集索引,和c2列的非聚集索引。另外创建了两个sp,分别是从t1中select数据和update数据。
好,打开一个新的查询窗口,我们开始执行下面的query:
-- Batch #2
USE deadlocktest
SET NOCOUNT ON
WHILE (1=1) EXEC p2 4
GO
开始执行后,然后我们打开第三个查询窗口,执行下面的query:
-- Batch #3
USE deadlocktest
SET NOCOUNT ON
CREATE TABLE #t1 (c2 int, c3 int)
GO
WHILE (1=1) BEGIN
INSERT INTO #t1 EXEC p1 4
TRUNCATE TABLE #t1
END
GO
开始执行,哈哈,很快,我们看到了这样的错误信息:
Msg 1205, Level 13, State 51, Procedure p1, Line 4
Transaction (Process ID 54) was deadlocked on lock resources with another process and has been chosen as the deadlock victim Rerun the transaction
spid54发现了死锁。
那么,我们该如何解决它?
在SqlServer 2005中,我们可以这么做:
1在trans3的窗口中,选择EXEC p1 4,然后right click,看到了菜单了吗?选择Analyse Query in Database Engine Tuning Advisor。
2注意右面的窗口中,wordload有三个选择:负载文件、表、查询语句,因为我们选择了查询语句的方式,所以就不需要修改这个radio option了。
3点左上角的Start Analysis按钮
4抽根烟,回来后看结果吧!出现了一个分析结果窗口,其中,在Index Recommendations中,我们发现了一条信息:大意是,在表t1上增加一个非聚集索引索引:t2+t1。
5在当前窗口的上方菜单上,选择Action菜单,选择Apply Recommendations,系统会自动创建这个索引。
重新运行batch #3,呵呵,死锁没有了。
这种方式,我们可以解决大部分的Sql Server死锁问题。那么,发生这个死锁的根本原因是什么呢?为什么增加一个non clustered index,问题就解决了呢? 这次,我们分析一下,为什么会死锁呢?再回顾一下两个sp的写法:
CREATE PROC p1 @p1 int AS
SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
GO
CREATE PROC p2 @p1 int AS
UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1
UPDATE t1 SET c2 = c2-1 WHERE c1 = @p1
GO
很奇怪吧!p1没有insert,没有delete,没有update,只是一个select,p2才是update。这个和我们前面说过的,trans1里面updata A,update B;trans2里面upate B,update A,根本不贴边啊!
那么,什么导致了死锁?
需要从事件日志中,看sql的死锁信息:
Spid X is running this query (line 2 of proc [p1], inputbuffer “… EXEC p1 4 …”):
SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
Spid Y is running this query (line 2 of proc [p2], inputbuffer “EXEC p2 4”):
UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1
The SELECT is waiting for a Shared KEY lock on index t1cidx The UPDATE holds a conflicting X lock
The UPDATE is waiting for an eXclusive KEY lock on index t1idx1 The SELECT holds a conflicting S lock
首先,我们看看p1的执行计划。怎么看呢?可以执行set statistics profile on,这句就可以了。下面是p1的执行计划
SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
|--Nested Loops(Inner Join, OUTER REFERENCES:([Uniq1002], [t1][c1]))
|--Index Seek(OBJECT:([t1][idx1]), SEEK:([t1][c2] >= [@p1] AND [t1][c2] <= [@p1]+(1)) ORDERED FORWARD)
|--Clustered Index Seek(OBJECT:([t1][cidx]), SEEK:([t1][c1]=[t1][c1] AND [Uniq1002]=[Uniq1002]) LOOKUP ORDERED FORWARD)
我们看到了一个nested loops,第一行,利用索引t1c2来进行seek,seek出来的那个rowid,在第二行中,用来通过聚集索引来查找整行的数据。这是什么?就是bookmark lookup啊!为什么?因为我们需要的c2、c3不能完全的被索引t1c1带出来,所以需要书签查找。
好,我们接着看p2的执行计划。
UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1
|--Clustered Index Update(OBJECT:([t1][cidx]), OBJECT:([t1][idx1]), SET:([t1][c2] = [Expr1004]))
|--Compute Scalar(DEFINE:([Expr1013]=[Expr1013]))
|--Compute Scalar(DEFINE:([Expr1004]=[t1][c2]+(1), [Expr1013]=CASE WHEN CASE WHEN
|--Top(ROWCOUNT est 0)
|--Clustered Index Seek(OBJECT:([t1][cidx]), SEEK:([t1][c1]=[@p1]) ORDERED FORWARD)
通过聚集索引的seek找到了一行,然后开始更新。这里注意的是,update的时候,它会申请一个针对clustered index的X锁的。
实际上到这里,我们就明白了为什么update会对select产生死锁。update的时候,会申请一个针对clustered index的X锁,这样就阻塞住了(注意,不是死锁!)select里面最后的那个clustered index seek。死锁的另一半在哪里呢?注意我们的select语句,c2存在于索引idx1中,c1是一个聚集索引cidx。问题就在这里!我们在p2中更新了c2这个值,所以sqlserver会自动更新包含c2列的非聚集索引:idx1。而idx1在哪里?就在我们刚才的select语句中。而对这个索引列的更改,意味着索引集合的某个行或者某些行,需要重新排列,而重新排列,需要一个X锁。
SO………,问题就这样被发现了。
总结一下,就是说,某个query使用非聚集索引来select数据,那么它会在非聚集索引上持有一个S锁。当有一些select的列不在该索引上,它需要根据rowid找到对应的聚集索引的那行,然后找到其他数据。而此时,第二个的查询中,update正在聚集索引上忙乎:定位、加锁、修改等。但因为正在修改的某个列,是另外一个非聚集索引的某个列,所以此时,它需要同时更改那个非聚集索引的信息,这就需要在那个非聚集索引上,加第二个X锁。select开始等待update的X锁,update开始等待select的S锁,死锁,就这样发生鸟。
那么,为什么我们增加了一个非聚集索引,死锁就消失鸟?我们看一下,按照上文中自动增加的索引之后的执行计划:
SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
|--Index Seek(OBJECT:([deadlocktest][dbo][t1][_dta_index_t1_7_2073058421__K2_K1_3]), SEEK:([deadlocktest][dbo][t1][c2] >= [@p1] AND [deadlocktest][dbo][t1][c2] <= [@p1]+(1)) ORDERED FORWARD)
哦,对于clustered index的需求没有了,因为增加的覆盖索引已经足够把所有的信息都select出来。就这么简单。
实际上,在sqlserver 2005中,如果用profiler来抓eventid:1222,那么会出现一个死锁的图,很直观的说。
下面的方法,有助于将死锁减至最少(详细情况,请看SQLServer联机帮助,搜索:将死锁减至最少即可。
按同一顺序访问对象。
避免事务中的用户交互。
保持事务简短并处于一个批处理中。
使用较低的隔离级别。
使用基于行版本控制的隔离级别。
将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON,使得已提交读事务使用行版本控制。
使用快照隔离。
使用绑定连接。
如果两个用户进程分别锁定了不同的资源 接着又试图锁定对方所锁定的资源 就会产生死锁 此时 SQL Server将自动地选择并中止其中一个进程以解除死锁 使得另外一个进程能够继续处理 系统将回退被中止的事务 并向被回退事务的用户发送错误信息 大多数设计良好的应用都会在接收到这个错误信息之后重新提交该事务 此时提交成功的可能性是很大的 但是 如果服务器上经常出现这种情况 就会显著地降低服务器性能 为避免死锁 设计应用应当遵循一定的原则 包括 ▲ 让应用每次都以相同的次序访问服务器资源 ▲ 在事务期间禁止任何用户输入 应当在事务开始之前收集用户输入 ▲ 尽量保持事务的短小和简单 ▲ 如合适的话 为运行事务的用户连接指定尽可能低的隔离级别 [适用于 ]此外 对于SQL Server的死锁问题 下面是几则实践中很有用的小技巧 ■ 使用SQL Server Profiler的Create Trace Wizard运行 Identify The Cause of a Deadlock 跟踪来辅助识别死锁问题 它将提供帮助查找数据库产生死锁原因的原始数据 [适用于 ]■ 如果无法消除应用中的所有死锁 请确保提供了这样一种程序逻辑 它能够在死锁出现并中止用户事务之后 以随机的时间间隔自动重新提交事务 这里等待时间的随机性非常重要 这是因为另一个竞争的事务也可能在等待 我们不应该让两个竞争的事务等待同样的时间 然后再在同一时间执行它们 这样的话将导致新的死锁 [适用于 ]■ 尽可能地简化所有T SQL事务 此举将减少各种类型的锁的数量 有助于提高SQL Server应用的整体性能 如果可能的话 应将较复杂的事务分割成多个较简单的事务 [适用于 ]■ 所有条件逻辑 变量赋值以及其他相关的预备设置 *** 作应当在事务之外完成 而不应该放到事务之内 永远不要为了接受用户输入而暂停某个事务 用户输入应当总是在事务之外完成 [适用于 ]■ 在存储过程内封装所有事务 包括BEGIN TRANSACTION和MIT TRANSACTION语句 此举从两个方面帮助减少阻塞的锁 首先 它限制了事务运行时客户程序和SQL Server之间的通信 从而使得两者之间的任何消息只能出现于非事务运行时间(减少了事务运行的时间) 其次 由于存储过程强制它所启动的事务或者完成 或者中止 从而防止了用户留下未完成的事务(留下未撤销的锁) [适用于 ]■ 如果客户程序需要先用一定的时间检查数据 然后可能更新数据 也可能不更新数据 那么最好不要在整个记录检查期间都锁定记录 假设大部分时间都是检查数据而不是更新数据 那么处理这种特殊情况的一种方法就是 先选择出记录(不加UPDATE子句 UPDATE子句将在记录上加上共享锁) 然后把它发送给客户 如果用户只查看记录但从来不更新它 程序可以什么也不做 反过来 如果用户决定更新某个记录 那么他可以通过一个WHERE子句检查当前的数据是否和以前提取的数据相同 然后执行UPDATE 类似地 我们还可以检查记录中的时间标识列(如果它存在的话) 如果数据相同 则执行UPDATE *** 作 如果记录已经改变 则应用应该提示用户以便用户决定如何处理 虽然这种方法需要编写更多的代码 但它能够减少加锁时间和次数 提高应用的整体性能 [适用于 ]■ 尽可能地为用户连接指定具有最少限制的事务隔离级别 而不是总是使用默认的READ MITTED 为了避免由此产生任何其他问题 应当参考不同隔离级别将产生的效果 仔细地分析事务的特性 [适用于 ] ■ 使用游标会降低并发性 为避免这一点 如果可以使用只读的游标则应该使用READ_ONLY游标选项 否则如果需要进行更新 尝试使用OPTIMISTIC游标选项以减少加锁 设法避免使用SCROLL_LOCKS游标选项 该选项会增加由于记录锁定引起的问题 [适用于 ]■ 如果用户抱怨说他们不得不等待系统完成事务 则应当检查服务器上的资源锁定是否是导致该问题的原因 进行此类检查时可以使用SQL Server Locks Object: Average Wait Time (ms) 用该计数器来度量各种锁的平均等待时间 如果可以确定一种或几种类型的锁导致了事务延迟 就可以进一步探究是否可以确定具体是哪个事务产生了这种锁 Profiler是进行这类具体分析的最好工具 [适用于 ]■ 使用sp_who和sp_who (SQL Server Books Online没有关于sp_who 的说明 但sp_who 提供了比sp_who更详细的信息)来确定可能是哪些用户阻塞了其他用户 [适用于 ]■ 试试下面的一个或多个有助于避免阻塞锁的建议 )对于频繁使用的表使用集簇化的索引 )设法避免一次性影响大量记录的T SQL语句 特别是INSERT和UPDATE语句 )设法让UPDATE和DELETE语句使用索引 )使用嵌套事务时 避免提交和回退冲突 [适用于 ] lishixinzhi/Article/program/SQLServer/201311/22222
exec sp_lock 快捷键 C_2
exec sp_who active exec sp_who快捷键 C_1
用Profiler里面的Locks->Deadlock graph 监控看看,如果看到了死锁图,就可以比较形象地展现死锁发生的过程,还可以看到锁的具体类型和过程里面的语句,对你诊断会有帮助。
Declare @LockTab table( spid int,dbid int ,ObjId int,IndId int ,Type varchar(50),Resource varchar(50),Mode varchar(50),Status varchar(50))
insert into @LockTab exec sp_lock
Declare @ActiveTab table(spid int,ecid int,status varchar(50),loginname varchar(50),hostname varchar(50),blk int,dbname varchar(50),cmd varchar(50),request_id int)
insert into @ActiveTab exec sp_who active
select from @LockTab lt
left join @ActiveTab at on ltspid=atspid
每个使用关系型数据库的程序都可能遇到数据死锁
的情况。理解什么是死锁之前先要了解锁定的概念:
多数情况下,可以认为如果一个资源被锁定,它总会在以后某个时间被释放。而死锁发生在当多个进程访问同一数据库时,其中每个进程拥有的锁都是其他进程所需的,由此造成每个进程都无法继续下去。简单的说,进程A等待进程B释放他的资源,B又等待A释放他的资源,这样就互相等待就形成死锁。
一、死锁的四个必要条件
1、互斥条件(Mutual
exclusion):资源不能被共享,只能由一个进程使用。
2、请求与保持条件(Hold
and
wait):已经得到资源的进程可以再次申请新的资源。
3、非剥夺条件(No
pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺。
4、循环等待条件(Circular
wait):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。
二、
避免死锁
1、按同一顺序访问对象
如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果两个并发事务获得
Supplier
表上的锁,然后获得
Part
表上的锁,则在其中一个事务完成之前,另一个事务被阻塞在
Supplier
表上。第一个事务提交或回滚后,第二个事务继续进行。不发生死锁。将存储过程用于所有的数据修改可以标准化访问对象的顺序。
2、避免事务中的用户交互
避免编写包含用户交互的事务,因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度,例如答复应用程序请求参数的提示。例如,如果事务正在等待用户输入,而用户去吃午餐了或者甚至回家过周末了,则用户将此事务挂起使之不能完成。这样将降低系统的吞吐量,因为事务持有的任何锁只有在事务提交或回滚时才会释放。即使不出现死锁的情况,访问同一资源的其它事务也会被阻塞,等待该事务完成。
3、保持事务简短并在一个批处理中
在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。
保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁。
4、使用低隔离级别
确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间,从而降低了锁定争夺。
5、使用绑定连接
使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作。次级连接所获得的任何锁可以象由主连接获得的锁那样持有,反之亦然,因此不会相互阻塞
6、SELECT语句加With(NoLock)提示
默认情况下SELECT语句会对查询到的资源加S锁(共享锁),S锁与X锁(排他锁)不兼容;但加上With(NoLock)后,SELECT不对查询到的资源加锁(或者加Sch-S锁,Sch-S锁可以与任何锁兼容);从而可以是这两条SQL可以并发地访问同一资源。当然,此方法适合解决读与写并发死锁的情况,但加With(NoLock)可能会导致脏读。
死锁是指两个或两个以上的进程因竞争资源而造成的一种互相等待的现象,若无外力作用,这些进程都将无法向前推进。
饥饿:由于长期得不到想要的资源而导致进程无法向前推进的现象。如在进程调度算法中的短进程优先算法中,若有源源不断的短进程到来,则长进程一直得不到处理机,从而发生了长进程饥饿。
死循环:某进程执行的过程中一直跳不出某种循环的现象。死循环可能是因为程序bug或者程序员有意为之。
(1) 互斥条件。 只有对必须互斥使用的资源争抢(如打印机设备)才能导致死锁。
(2) 不剥夺条件。 进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。如果进程可以抢夺其他进程持有自己需要的资源的话,也就不会产生死锁了,需要资源直接抢就行了。
(3) 请求和保持条件。 进程已经保持了至少一个资源,但是又提出了新的资源需求,而该资源又被其他进程占有,此时请求进程被阻塞,但是又对自己持有的资源保持不放。也是很简单的道理,如果一个进程请求的资源被阻塞,就释放了自己持有的资源,其他进程就可以获取它释放的资源,也就不会发生相互等待而导致死锁了。
(4) 循环等待条件。 存在一种进程资源的循环等待链,链中的每一个进程已经获得的资源同时被下一个进程所请求。
(1) 对系统资源的争抢。各进程对不可剥夺资源的竞争可能会引起死锁。
(2) 进程推进顺序非法。如果请求资源的顺序不当也会导致死锁,如上面的图,双方请求的资源被对方占有而导致死锁。
(3) 信号量的使用不当。并发执行进程时,如果信号量使用的顺序不当也会到导致死锁。
总之,对不可剥夺的资源的不合理分配,可能导致死锁。
(1) 预防死锁。 破坏死锁产生的四个必要条件中的一个或几个。
(2) 避免死锁。 用某种方法防止系统进入安全状态,从而避免死锁(银行家算法)。
(3) 死锁的检测和解除。 允许死锁的发生,不过 *** 作系统会负责检测出死锁的发生,然后才去某种措施解除死锁。
如果把只能互斥使用的资源改造成允许共享使用,则系统不会进入死锁状态。如 SPOOLing技术 。 *** 作系统可以采用SPOOLing技术吧独占设备在逻辑上改造成共享设备。 它由专门负责 I/O 的常驻内存进程以及输入、输出井组成。 比如,用SPOOLing技术将打印机改造为共享设备
缺点:并不是所有的资源都可以改造成共享使用的资源。并且为了系统安全,有时候还需要保护这种互斥性。
方案一:当某个进程请求新的资源得不到满足时,它必须立即释放保持的所有资源,待以后需要时重新申请。
方案二:申请的资源被其他进程占用时,借助 *** 作系统的协助,剥夺进程资源,至于剥夺哪个进程资源可以根据优先级考虑。
缺点:实现复杂。暂时请求不到某个资源,之前获取的资源就需要释放,以后重新申请,如果一直这样可能会导致饥饿。
可以采用 静态分配方法 ,即进程在运行前一次申请完它所需要的全部资源,在它的资源未满足之前,不让它运行。一旦运行,这些资源在运行期间一直归它所有,该进程就不会再请求别的任何资源。
缺点:如果有些资源只需要用很短的时间,因此如果进程运行时间长,在运行期间都会保持持有所有资源,就会造成资源浪费, 资源利用率低。 此外,该策略可能导致某些进程饥饿。如下,如果C类进程需要资源1和资源2,如果有大量的A或B类进程,就会导致C类进程一直不能一次获取全部需要的资源,导致饥饿。
可采用 顺序资源分配法。 首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,同类资源一次申请完。
原理:一个进程只要已占有我号的资源时,才有资格申请更大号编号的资源。按照这样的规则,已持有大编号的资源就不会逆向回来申请我号的资源,从而就不会产生循环等待的现象。
缺点:
(1) 实现复杂。
(2) 不方便增加新的设备,因为要重新分配所有编号。
(3) 进程实际使用资源的顺序可能和资源编号递增顺序不一致,会导致资源浪费。例如打印机设备编号2,输出设备编号为1,那么一个进程请求打印机设备前,必须先请求输出设备,而输出设备在请求打印机设备这一段时间内根本没有发挥作用,其他进程也不能访问,造成资源浪费。
假如你是一个成功的银行家,手里有100个小目标资金现在有三家公司A、B、C分别向从你贷款
然而,有个规则,如果借给企业的钱达不到企业提出的最大需求,那么钱可能就一去不复返了
刚开始,三家公司分别从你这里借了20、10、30亿
此时,手里还要40亿,此时B表示还要借20亿,那么你需要考虑可以借吗?
如果给B借了20亿此时手里还要有20亿。
之后看手里剩下的钱能不能周转过来,现在可以将剩下的20亿借给C,C就达到了最大需求,之后C还钱50亿,然后借给A,A达到最大需求,最后A还70亿,最后A还好借给B20亿后,可以通过 C->A->B 这个顺序周转,所以这20亿可以借。
现在看另一种情况,回到最初状态,现在手里还有40亿
对于上面的第一种借给B20亿是安全的,因为会存在C->A->B这样的周转路径(安全序列),不会血本无归。
安全序列:如果系统按照这种序列分配资源,则每个进程都能顺利完成。 只要能找出一个安全序列,系统就是 安全的 。一个系统的 安全序列可能有多个 。
如果分配了资源之后,系统中找不到任何一个安全序列,系统进入了 不安全状态 ,这就意味着之后可能所有的进程都无法顺序执行下去。当然,如果有进程提前归还了一些资源(如对上面的第二中情况,如果B提前还10亿,那么就可以周转了),那系统也可能 重新回到安全状态 ,不过在分配资源之前总是考虑最坏情况 。
如果系统处于 安全状态 ,就 一定不会发生死锁 。如果系统进入了不安全状态未必一定发生死锁,但是发生了死锁一定是在不安全状态。
银行家算法 核心思想: 在进程提出资源请求时,先预先判断此次分配是否会导致系统进入不安全状态,如果进入不安全状态,就暂时不答应这次请求,让该进程先阻塞。
银行家算法步骤:
如果系统中既不采取预防死锁的措施,也不采取避免死锁的措施,系统就很可能发生死锁。在这种情况下,系统应当提供两个算法:
(1) 死锁检测算法:用于检测系统状态,以确定系统中是否发生了死锁。
(2) 死锁解除算法:当认定系统中已经发生了死锁,利用该算法可将系统从死锁状态中解脱出来。
系统死锁可利用 资源分配图 来描述。
上图可以看到,R1类资源的资源数已经全部分配完了,R2类资源还有一个资源。P1进程向R2类资源请求一个资源,P2进程向R1类资源请求一个资源。
如果系统中剩余可用的资源数足够满足进程的需求,那么这个进程暂时是不会阻塞的,可以顺利的执行下去。如果这个进程执行结束了把资源归还给系统,就可能使某些正在等待的资源的进程重新被激活,并顺利的执行下去。相应的,这些被激活的进程执行完后又会归还一些资源,这样可能又会激活另外一些阻塞的进程
按照上面的过程分析,最终 能消除所有边 ,就称这个图是 可完全简化的 。此时一定 没有发生死锁 (相当于找到一个安全序列)。如果最终 不能消除所有边 ,那么此时就是 发生死锁了。
最终还连着边的进程就是处于死锁状态的进程。
检测死锁的算法:
死锁定理:如果某时刻系统的资源分配图是不可完全简化的,那么此时系统死锁。
下图是一个系统死锁的图:
即使P3释放了资源,P1和P2进程都不满足继续运行的条件,所以此时P1和P2就是死锁进程。
解除死锁的方法有:
(1) 资源剥夺法。 挂起(暂时放在外存上)某些死锁进程,并抢占它的资源,将这些资源分配给其他进程,但是应防止被挂起长时间导致饥饿。
(2) 撤销进程法。 强制撤销部分、甚至全部死锁进程,并剥夺这些进程的资源。这中方法实现简单,但是也是有代价的,如果有些已经运行了很长时间了,离成功只有一步之遥了,此时撤销导致功亏一篑,还需要从头再来
(3) 进程回退法。 让一个或多个死锁进程回退到足以避免死锁的地步。这就要求系统要记录进程的历史信息,设置还原点。
那么对哪些进程动手呢?可以考虑优先对以下的进程处理:
(1) 优先级低的进程。
(2) 执行时间段的进程。
(3) 距离运行结束剩余时间长的进程。
(4) 使用资源多的进程。
(5) 批处理式而不是交互式的进程。
可以用sp_who'active'看一下午blk字段是否为0,如是其它数x,说明这个数可能就是锁,再用sp_who数x看一下它下面的blk是否有数,这样查下去,如果它下面没有数并且是查询状态或是等待状态等(除更新及插入状态)都可以用kill数x
以上就是关于如何处理SQL Server死锁问题全部的内容,包括:如何处理SQL Server死锁问题、减少SQL Server数据库死锁的技巧、怎么查看数据库死锁,和解决方法等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)