happyhippy
作者:
Silent Void
出处:
http://happyhippy.cnblogs.com/
转载须保留此声明,并注明在文章起始位置给出原文链接。
1.
死锁原理
根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。
死锁的四个必要条件:
互斥条件
(Mutual exclusion)
:资源不能被共享,只能由一个进程使用。
请求与保持条件
(Hold and wait)
:已经得到资源的进程可以再次申请新的资源。
非剥夺条件
(No pre-emption)
:已经分配的资源不能从相应的进程中被强制地剥夺。
循环等待条件
(Circular wait)
:系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。
对应到
SQL Server
中,当在两个或多个任务中,如果每个任务锁定了其他任务试图锁定的资源,此时会造成这些任务永久阻塞,从而出现死锁;这些资源可能是:单行
(RID
,堆中的单行
)
、索引中的键
(KEY
,行锁
)
、页
(PAG
,
8KB)
、区结构
(EXT
,连续的
8
页
)
、堆或
B
树
(HOBT)
、表
(TAB
,包括数据和索引
)
、文件
(File
,数据库文件
)
、应用程序专用资源
(APP)
、元数据
(METADATA)
、分配单元
(Allocation_Unit)
、整个数据库
(DB)
。
一个死锁示例如下图所示:
说明:
T1
、
T2
表示两个任务;
R1
和
R2
表示两个资源;由资源指向任务的箭头
(
如
R1->T1
,
R2->T2)
表示该资源被改任务所持有;由任务指向资源的箭头
(
如
T1->S2
,
T2->S1)
表示该任务正在请求对应目标资源;
其满足上面死锁的四个必要条件:
(1).
互斥:资源
S1
和
S2
不能被共享,同一时间只能由一个任务使用;
(2).
请求与保持条件:
T1
持有
S1
的同时,请求
S2
;
T2
持有
S2
的同时请求
S1
;
(3).
非剥夺条件:
T1
无法从
T2
上剥夺
S2
,
T2
也无法从
T1
上剥夺
S1
;
(4).
循环等待条件:上图中的箭头构成环路,存在循环等待。
2.
死锁排查
(1).
使用
SQL Server
的系统存储过程
sp_who
和
sp_lock
,可以查看当前数据库中的锁情况;进而根据
objectID(@objID)(SQL Server 2005)/ object_name(@objID)(Sql Server 2000)
可以查看哪个资源被锁,用
dbcc ld(@blk)
,可以查看最后一条发生给
SQL Server
的
Sql
语句;
<!-- <br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->
CREATE
Table
#Who(spid
int
,
ecid
int
,
status
nvarchar
(
50
),
loginname
nvarchar
(
50
),
hostname
nvarchar
(
50
),
blk
int
,
dbname
nvarchar
(
50
),
cmd
nvarchar
(
50
),
request_ID
int
);
CREATE
Table
#Lock(spid
int
,
dpid
int
,
objid
int
,
indld
int
,
[
Type
]
nvarchar
(
20
),
Resource
nvarchar
(
50
),
Mode
nvarchar
(
10
),
Status
nvarchar
(
10
)
);
INSERT
INTO
#Who
EXEC
sp_who active
--
看哪个引起的阻塞,blk
INSERT
INTO
#Lock
EXEC
sp_lock
--
看锁住了那个资源id,objid
DECLARE
@DBName
nvarchar
(
20
);
SET
@DBName
=
'
NameOfDataBase
'
SELECT
#Who.
*
FROM
#Who
WHERE
dbname
=
@DBName
SELECT
#Lock.
*
FROM
#Lock
JOIN
#Who
ON
#Who.spid
=
#Lock.spid
AND
dbname
=
@DBName
;
--
最后发送到SQL Server的语句
DECLARE
crsr
Cursor
FOR
SELECT
blk
FROM
#Who
WHERE
dbname
=
@DBName
AND
blk
<>
0
;
DECLARE
@blk
int
;
open
crsr;
FETCH
NEXT
FROM
crsr
INTO
@blk
;
WHILE
(
@@FETCH_STATUS
=
0
)
BEGIN
;
dbcc
inputbuffer(
@blk
);
FETCH
NEXT
FROM
crsr
INTO
@blk
;
END
;
close
crsr;
DEALLOCATE
crsr;
--
锁定的资源
SELECT
#Who.spid,hostname,objid,
[
type
]
,mode,
object_name
(objid)
as
objName
FROM
#Lock
JOIN
#Who
ON
#Who.spid
=
#Lock.spid
AND
dbname
=
@DBName
WHERE
objid
<>
0
;
DROP
Table
#Who;
DROP
Table
#Lock;
(2).
使用
SQL Server Profiler
分析死锁
:
将
Deadlock graph
事件类添加到跟踪。此事件类使用死锁涉及到的进程和对象的
XML
数据填充跟踪中的
TextData
数据列。
SQL Server
事件探查器
可以将
XML
文档提取到死锁
XML (.xdl)
文件中,以后可在
SQL Server Management Studio
中查看该文件。
3.
避免死锁
上面
1
中列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件,就可以避免死锁发生,一般有以下几种方法
(FROM Sql Server 2005
联机丛书
)
:
(1).
按同一顺序访问对象。
(
注:避免出现循环
)
(2).
避免事务中的用户交互。
(
注:减少持有资源的时间,较少锁竞争
)
(3).
保持事务简短并处于一个批处理中。
(
注:同
(2)
,减少持有资源的时间
)
(4).
使用较低的隔离级别。
(
注:使用较低的隔离级别(例如已提交读)比使用较高的隔离级别(例如可序列化)持有共享锁的时间更短,减少锁竞争
)
(5).
使用基于行版本控制的隔离级别
:
2005
中支持快照事务隔离和指定
READ_COMMITTED
隔离级别的事务使用行版本控制,可以将读与写操作之间发生的死锁几率降至最低:
SET ALLOW_SNAPSHOT_ISOLATION ON --
事务可以指定
SNAPSHOT
事务隔离级别
;
SET READ_COMMITTED_SNAPSHOT ON
--
指定
READ_COMMITTED
隔离级别的事务将使用行版本控制而不是锁定。默认情况下
(
没有开启此选项,没有加
with nolock
提示
)
,
SELECT
语句会对请求的资源加
S
锁
(
共享锁
)
;而开启了此选项后,
SELECT
不会对请求的资源加
S
锁。
注意:
设置
READ_COMMITTED_SNAPSHOT
选项时,数据库中只允许存在执行
ALTER DATABASE
命令的连接。在
ALTER DATABASE
完成之前,数据库中决不能有其他打开的连接。数据库不必一定要处于单用户模式中。
(6).
使用绑定连接
。
(
注:绑定会话有利于在同一台服务器上的多个会话之间协调操作。绑定会话允许一个或多个会话共享相同的事务和锁
(
但每个回话保留其自己的事务隔离级别
)
,并可以使用同一数据,而不会有锁冲突。可以从同一个应用程序内的多个会话中创建绑定会话,也可以从包含不同会话的多个应用程序中创建绑定会话。在一个会话中开启事务
(begin tran)
后,调用
exec sp_getbindtoken @Token out;
来取得
Token
,然后传入另一个会话并执行
EXEC sp_bindsession @Token
来进行绑定
(
最后的示例中演示了绑定连接
)
。
4.
死锁处理方法:
(1).
根据
2
中提供的
sql
,查看那个
spid
处于
wait
状态,然后用
kill spid
来干掉
(
即破坏死锁的第四个必要条件
:
循环等待
)
;当然这只是一种临时解决方案,我们总不能在遇到死锁就在用户的生产环境上排查死锁、
Kill sp
,我们应该考虑如何去避免死锁。
(2).
使用
SET LOCK_TIMEOUT timeout_period(
单位为毫秒
)
来设定锁请求超时
。默认情况下,数据库没有超时期限
(timeout_period
值为
-1
,可以用
SELECT @@LOCK_TIMEOUT
来查看该值,即无限期等待
)
。当请求锁超过
timeout_period
时,将返回错误。
timeout_period
值为
0
时表示根本不等待,一遇到锁就返回消息。设置锁请求超时,破环了死锁的第二个必要条件
(
请求与保持条件
)
。
<!-- <br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->服务器: 消息
1222
,级别
16
,状态
50
,行
1
已超过了锁请求超时时段。
(3). SQL Server
内部有一个锁监视器线程执行死锁检查
,锁监视器对特定线程启动死锁搜索时,会标识线程正在等待的资源;然后查找特定资源的所有者,并递归地继续执行对那些线程的死锁搜索,直到找到一个构成死锁条件的循环。检测到死锁后,数据库引擎
选择运行回滚开销最小的事务的会话作为死锁牺牲品,返回
1205
错误,回滚死锁牺牲品的事务并释放该事务持有的所有锁,使其他线程的事务可以请求资源并继续运行。
5.
两个死锁示例及解决方法
5.1 SQL
死锁
(1).
测试用的基础数据:
(2).
开两个查询窗口,分别执行下面两段
sql
上面的
SQL
中有一句
WaitFor Delay '00:01:00'
,用于等待
1
分钟,以方便查看锁的情况。
(3).
查看锁情况
在执行上面的
WaitFor
语句期间,执行第二节中提供的语句来查看锁信息:
Query1
中,持有
Lock1
中第一行
(
表中只有一行数据
)
的行排他锁
(RID:X)
,并持有该行所在页的意向更新锁
(PAG:IX)
、该表的意向更新锁
(TAB:IX)
;
Query2
中,持有
Lock2
中第一行
(
表中只有一行数据
)
的行排他锁
(RID:X)
,并持有该行所在页的意向更新锁
(PAG:IX)
、该表的意向更新锁
(TAB:IX)
;
执行完
Waitfor
,
Query1
查询
Lock2
,请求在资源上加
S
锁,但该行已经被
Query2
加上了
X
锁;
Query2
查询
Lock1
,请求在资源上加
S
锁,但该行已经被
Query1
加上了
X
锁;于是两个查询持有资源并互不相让,构成死锁。
(4).
解决办法
a). SQL Server
自动选择一条
SQL
作死锁牺牲品
:运行完上面的两个查询后,我们会发现有一条
SQL
能正常执行完毕,而另一个
SQL
则报如下错误:
<!-- <br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->服务器: 消息
1205
,级别
13
,状态
50
,行
1
事务(进程 ID xx)与另一个进程已被死锁在 lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。
这就是上面第四节中介绍的锁监视器
干活了。
b).
按同一顺序访问对象:
颠倒任意一条
SQL
中的
Update
与
SELECT
语句的顺序。例如修改第二条
SQL
成如下:
当然这样修改也是有代价的,这会导致第一条
SQL
执行完毕之前,第二条
SQL
一直处于阻塞状态。单独执行
Query1
或
Query2
需要约
1
分钟,但如果开始执行
Query1
时,马上同时执行
Query2
,则
Query2
需要
2
分钟才能执行完;这种按顺序请求资源从一定程度上降低了并发性。
c). SELECT
语句加
With(NoLock)
提示
:默认情况下
SELECT
语句会对查询到的资源加
S
锁
(
共享锁
)
,
S
锁与
X
锁
(
排他锁
)
不兼容;但加上
With(NoLock)
后,
SELECT
不对查询到的资源加锁
(
或者加
Sch-S
锁,
Sch-S
锁可以与任何锁兼容
)
;从而可以是这两条
SQL
可以并发地访问同一资源。当然,此方法适合解决读与写并发死锁的情况,但加With(NoLock)可能会导致脏读。
<!-- <br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->SELECT
*
FROM
Lock2
WITH
(NOLock)
SELECT
*
FROM
Lock1
WITH
(NOLock)
d).
使用较低的隔离级别。
SQL Server 2000
支持四种事务处理隔离级别
(TIL)
,分别为:
READ UNCOMMITTED
、
READ COMMITTED
、
REPEATABLE READ
、
SERIALIZABLE
;
SQL Server 2005
中增加了
SNAPSHOT TIL
。默认情况下,
SQL Server
使用
READ COMMITTED TIL
,我们可以在上面的两条
SQL
前都加上一句
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
,来降低
TIL
以避免死锁
;事实上,运行在
READ UNCOMMITTED TIL
的事务,其中的
SELECT
语句不对结果资源加锁或加
Sch-S
锁,而不会加
S
锁;但还有一点需要注意的是:
READ UNCOMMITTED TIL
允许脏读
,虽然加上了降低
TIL
的语句后,上面两条
SQL
在执行过程中不会报错,但执行结果是一个返回
1
,一个返回
2
,即读到了脏数据,也许这并不是我们所期望的。
e).
在
SQL
前加
SET LOCK_TIMEOUT timeout_period
,当请求锁超过设定的
timeout_period
时间后,就会终止当前
SQL
的执行,牺牲自己,成全别人。
f).
使用基于行版本控制的隔离级别
(SQL Server 2005
支持
)
:开启下面的选项后,
SELECT
不会对请求的资源加
S
锁,不加锁或者加
Sch-S
锁,从而将读与写操作之间发生的死锁几率降至最低;而且不会发生脏读。
啊
<!-- <br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->SET
ALLOW_SNAPSHOT_ISOLATION
ON
SET
READ_COMMITTED_SNAPSHOT
ON
g).
使用绑定连接
(
使用方法见下一个示例。
)
5.2
程序死锁
(SQL
阻塞
)
看一个例子:一个典型的数据库操作事务死锁分析
,按照我自己的理解,我觉得这应该算是
C#
程序中出现死锁,而不是数据库中的死锁;下面的代码模拟了该文中对数据库的操作过程:
<!-- <br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->
//
分享到:
相关推荐
SQLServer数据库死锁介绍,以及使用独立的事务模拟死锁的产生。
一、死锁原理 死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所占用的资源而处于的一种永久等待状态。为了解释死锁的原理,我们需要了解四个必要条件: 1. 互斥条件 (Mutual ...
通过对死锁原理的理解、使用Oracle提供的内置工具进行诊断以及实施有效的预防措施,可以有效地管理和减少死锁的发生,从而提高系统的整体性能和稳定性。希望以上内容能够帮助您更好地理解和处理Oracle数据库中的查询...
死锁原理分析 ##### 2.1 多线程工作原理 多线程模型允许多个线程并行执行任务,提高了系统的执行效率。相比单线程模型,多线程模型具有以下优点: - **并行处理能力**:可以将长时间运行的任务放到后台处理,提高...
### 死锁详解:操作系统中的幽灵 #### 死锁概览 死锁,作为操作系统领域内一个核心而复杂的问题,常被视为系统设计者...无论是教育还是实践层面,对死锁原理及应对措施的掌握都是IT专业人员不可或缺的知识技能之一。
1. 死锁原理 死锁的产生基于四个基本条件: - **互斥条件**:资源不能同时被多个事务使用,必须独占。 - **请求与保持条件**:已获得资源的事务可以请求更多资源,而不是释放已有的。 - **非剥夺条件**:资源...
### 死锁原理 死锁是并发数据库操作中的一种特殊情况,当多个事务在执行过程中因争夺资源而互相等待对方释放资源,导致事务都无法继续执行的状态。在Oracle中,死锁通常发生在事务试图更新或删除数据库表中同一列的...
本教程将通过C语言来实现一个死锁检测组件,这对于理解死锁原理以及如何预防和解决死锁问题具有极大的帮助。 首先,我们要了解死锁的基本条件:互斥条件、占有并等待、无剥夺和循环等待。互斥条件意味着资源只能由...
然后,根据死锁原理,找出既不阻塞又非独立的进程结点,释放该进程的占用资源,并继续寻找下一个孤立结点。如果所有进程都能放入孤立数组中,则系统不会发生死锁。 在代码中,我们使用了多个数组来存储进程的信息,...
### 二、死锁原理 死锁的产生通常是由于两个并发事务A和B,A持有资源X并请求资源Y,而B持有资源Y并请求资源X,导致双方都无法继续执行,陷入等待状态。这种情况下,如果没有死锁检测机制,系统可能会无限期地等待...
1. **死锁原理** 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种僵局。 2. **死锁预防** 死锁预防是一种避免死锁产生的策略。 3. **死锁避免** 死锁避免是通过资源分配策略来避免死锁发生...
二、死锁的原理 当对于数据库某个表的某一列做更新或删除等操作,执行完毕后该条语句不提 交,另一条对于这一列数据做更新操作的语句在执行的时候就会处于等待状态, 此时的现象是这条语句一直在执行,但一直没有...
总的来说,理解并掌握DB2中死锁的原理、预防和解决方法,对于保证数据库系统的稳定运行至关重要。通过持续监控、优化事务处理和数据库配置,我们可以有效地减少和解决DB2的死锁问题。在实践中,应结合具体的业务场景...
最后,本文还将对死锁原理和分析进行了详细的解释。 一、背景 MySQL/InnoDB 的加锁分析一直是一个比较困难的话题。了解 MySQL 加锁机制对数据库性能和可靠性的影响非常重要。本文将对 MySQL/InnoDB 的加锁机制进行...