当前位置:首页 » 编程语言 » sql避免死锁
扩展阅读
webinf下怎么引入js 2023-08-31 21:54:13
堡垒机怎么打开web 2023-08-31 21:54:11

sql避免死锁

发布时间: 2023-05-11 15:18:20

sql:下面哪个锁防止你的数据库锁死

答案是B,更新锁!解答如下:
共享锁
共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时,任何其它事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享 (S) 锁,除非将事务隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享 (S) 锁。

更新锁
更新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。

若要避免这种潜在的死锁问题,请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。

排它锁
排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据。

意向锁
意向锁表示 SQL Server 需要在层次结构中的某些底层资源上获取共享 (S) 锁或排它 (X) 锁。例如,放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享 (S) 锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它 (X) 锁。意向锁可以提高性能,因为 SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。

意向锁包括意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。

⑵ 数据库中死锁是什么产生的

数据库操作的死锁是不可避免的,本文并不打算讨论死锁如何产生,重点在于解决死锁,通过SQL Server 2005, 现在似乎有了一种新的解决办法。

将下面的SQL语句放在两个不同的连接里面,并且在5秒内同时执行,将会发生死锁。

use Northwind
begin tran
insert into Orders(CustomerId) values(@#ALFKI@#)
waitfor delay @#00:00:05@#
select * from Orders where CustomerId = @#ALFKI@#
commit
print @#end tran@#

SQL Server对付死锁的办法是牺牲掉其中的一个,抛出异常,并且回滚事务。在SQL Server 2000,语句一旦发生异常,T-SQL将不会继续运行,上面被牺牲的连接中, print @#end tran@#语句将不会被运行,所以我们很难在SQL Server 2000的T-SQL中对死锁进行进一步的处理。

现在不同了,SQL Server 2005可以在T-SQL中对异常进行捕获,这样就给我们提供了一条处理死锁的途径:

下面利用的try ... catch来解决死锁。

SET XACT_ABORT ON
declare @r int
set @r = 1
while @r <= 3
begin
begin tran

begin try
insert into Orders(CustomerId) values(@#ALFKI@#)
waitfor delay @#00:00:05@#
select * from Orders where CustomerId = @#ALFKI@#

commit
break
end try

begin catch
rollback
waitfor delay @#00:00:03@#
set @r = @r + 1
continue
end catch
end

解决方法当然就是重试,但捕获错误是前提。rollback后面的waitfor不可少,发生冲突后需要等待一段时间,@retry数目可以调整以应付不同的要求。

但是现在又面临一个新的问题: 错误被掩盖了,一但问题发生并且超过3次,异常却不会被抛出。SQL Server 2005 有一个RaiseError语句,可以抛出异常,但却不能直接抛出原来的异常,所以需要重新定义发生的错误,现在,解决方案变成了这样:

declare @r int
set @r = 1
while @r <= 3
begin
begin tran

begin try
insert into Orders(CustomerId) values(@#ALFKI@#)
waitfor delay @#00:00:05@#
select * from Orders where CustomerId = @#ALFKI@#

commit
break
end try

begin catch
rollback
waitfor delay @#00:00:03@#
set @r = @r + 1
continue
end catch
end
if ERROR_NUMBER() <> 0
begin
declare @ErrorMessage nvarchar(4000);
declare @ErrorSeverity int;
declare @ErrorState int;
select
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
raiserror (@ErrorMessage,
@ErrorSeverity,
@ErrorState
);
end

⑶ SQL SERVER怎样才能有效避免死锁

你可以晌滑态采取以下方法将死锁减至最少:
•按同一顺序访问对象。
•避免事务中的用户交互让伏。
•保持事务简短并处于一个批处理中。
•使用较低的隔离级别。
•使用基于行版本控制的隔离级别。
a.将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON,使得已提交读事务使用行版本控制。
b.使用快照隔离宴源。
•使用绑定连接。

⑷ SQLServer死锁的解除方法

SQL Server死锁使我们经常遇到的问题 下面就为您介绍如何查询SQL Server死锁 希望对您学习SQL Server死锁方面能有所帮助

SQL Server死锁的查询方法

exec master dbo p_lockinfo 显示死锁的进程 不显示正常的进程

exec master dbo p_lockinfo 杀死死锁的进程 不显示正常的进程

SQL Server死锁的解除方法

Create proc p_lockinfo

@kill_lock_spid bit= 是否杀掉死锁的进程 杀掉 仅显示

@show_spid_if_nolock bit= 如果没有死锁的进程 是否显示正常进程信息 显示 不显示

as

declare @count int @s nvarchar( ) @i int

select id=identity(int ) 标志

进程ID=spid 线程ID=kpid 块进程ID=blocked 数据库ID=dbid

数据库名=db_name(dbid) 用户ID=uid 用户名=loginame 累计CPU时间=cpu

登陆时间=login_time 打开事务数=open_tran 进程状态=status

工作站名=hostname 应用程序名=program_name 工作站进程ID=hostprocess

域名=nt_domain 网卡地址=net_address

into #t from(

select 标志= 死锁的进程

spid kpid a blocked dbid uid loginame cpu login_time open_tran

status hostname program_name hostprocess nt_domain net_address

s =a spid s =

from mastersysprocesses a join (

select blocked from mastersysprocesses group by blocked

)b on a spid=b blocked where a blocked=

union all

select |_牺牲品_>

spid kpid blocked dbid uid loginame cpu login_time open_tran

status hostname program_name hostprocess nt_domain net_address

s =blocked s =

from mastersysprocesses a where blocked<>

)a order by s s

select @count=@@rowcount @i=

if @count= and @show_spid_if_nolock=

begin

insert #t

select 标志= 正常的进程

spid kpid blocked dbid db_name(dbid) uid loginame cpu login_time

open_tran status hostname program_name hostprocess nt_domain net_address

from mastersysprocesses

set @count=@@rowcount

end

if @count>

begin

create table #t (id int identity( ) a nvarchar( ) b Int EventInfo nvarchar( ))

if @kill_lock_spid=

begin

declare @spid varchar( ) @标志 varchar( )

while @i<=@count

begin

select @spid=进程ID @标志=标志 from #t whereid=@i

insert #t exec( dbcc inputbuffer( +@spid+ ) )

if @标志= 死锁的进程 exec( kill +@spid)

set @i=@i+

end

end

else

while @i<=@count

begin

select @s= dbcc inputbuffer( +cast(进程ID as varchar)+ ) from #t whereid=@i

insert #t exec(@s)

set @i=@i+

end

select a * 进程的SQL语句=b EventInfo

from #t a join #t b on a id=b id

lishixin/Article/program/SQLServer/201311/22183

⑸ 如何减少SQL Server死锁发生

死锁是指在某组资源中 两个或两个以上的线程在执行过程中 在争夺某一资源时而造成互相等待的现象 若无外力的作用下 它们都将无法推进下去 死时就可能会产生死锁 这些永远在互相等待的进程称为死锁线程 简单的说 进程A等待进程B释放他的资源 B又等待A释放他的资源 这样互相等待就形成死锁

如在数据库中 如果需要对一条数据进行修改 首先数据库管理系统会在上面加锁 以保证在同一时间只迹扒瞎有一个事务能进行修改操作 如事务 的线程 T 具有表A上的排它锁 事务 的线程T 具有表B上的排它锁 并且之后需要表A上的锁 事务 无法获得这一锁 因为事务 已拥有它 事务 被阻塞 等待事务 然后 事务 需要表B的锁 但无法获得锁 因为事务 将它锁定了 事务在提交或回滚之前不能释放持有的锁 因为事务需要对方控制的锁才能继续操作 所以它们不能提交或回滚 这样数据库就会发生死锁了

如在编此笑写存储过程的时候 由于有些存储过程事务性姿空的操作比较频繁 如果先锁住表A 再锁住表B 那么在所有的存储过程中都要按照这个顺序来锁定它们 如果无意中某个存储过程中先锁定表B 再锁定表A 这可能就会导致一个死锁 而且死锁一般是不太容易被发现的

如果服务器上经常出现这种死锁情况 就会降低服务器的性能 所以应用程序在使用的时候 我们就需要对其进行跟踪 使用sp_who和sp_who 来确定可能是哪些用户阻塞了其他用户 我们还可以用下面的存储过程来跟踪具体的死锁执行的影响

create procere sp_who_lock

as

begin

declare @spid int @bl int @intTransactionCountOnEntry

int @intRowcount

int @intCountProperties

int @intCounter

int create table

#tmp_lock_who

(id int identity( ) spid *** allint bl *** allint)IF @@ERROR<> RETURN

@@ERRORinsert into

#tmp_lock_who(spid bl) select

blockedfrom (select * from sysprocesses where

blocked> )

a where not exists(select * from (select * from sysprocesses where blocked> )

b where a blocked=spid)union select spid blocked from sysprocesses where

blocked> IF

@@ERROR<> RETURN @@ERROR 找到临时表的记录数select

@intCountProperties = Count(*) @intCounter = from #tmp_lock_whoIF

@@ERROR<> RETURN @@ERROR if @intCountProperties= select

现在没有阻塞和死锁信息

as message 循环开始while @intCounter <= @intCountPropertie *** egin 取第一条记录select

@spid = spid @bl = blfrom #tmp_lock_who where id = @intCounter beginif @spid = select

引起数据库死锁的是: + CAST(@bl AS VARCHAR( )) + 进程号

其执行的SQL语法如下 elseselect

进程号SPID + CAST(@spid AS VARCHAR( ))+ 被 +

进程号SPID + CAST(@bl AS VARCHAR( )) + 阻塞

当前进程执行的SQL语法如下 DBCC INPUTBUFFER (@bl )end

循环指针下移set @intCounter = @intCounter + enddrop table #tmp_lock_who

return

我们只需要通过在查询分析器里面执行sp_who_lock 就可以具体捕捉到执行的堵塞进程 这时我们就可以对对应的SQL语句或者存储过程进行性能上面的改进及设计

所以我们在数据库设计的时候 虽然不能完全避免死锁 但可以使死锁的数量尽量减少 增加事务的吞吐量并减少系统开销 因为只有很少的事务 所以就得遵循下面的原则

按同一顺序访问对象

如果所有并发事务按同一顺序访问对象 则发生死锁的可能性会降低 在写SQL语句或存储过程的时候 就需要按照顺序在两个并发事务中先获得表A上的锁 然后获得表B上的锁 当第一个事务完成之前 另一个事务被阻塞在表A上 第一个事务提交或回滚后 第二个事务继续进行 而不能在语句里面写先获得表B上的锁 然后再获得表A的锁

避免事务中的用户交互

避免编写包含用户交互的事务 因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度 例如答复应用程序请求参数的提示 例如 如果事务正在等待用户输入 而用户就去做别的事了 则用户将此事务挂起使之不能完成 这样将降低系统的吞吐量 因为事务持有的任何锁只有在事务提交或回滚时才会释放 即使不出现死锁的情况 访问同一资源的其它事务也会被阻塞 等待该事务完成

保持事务简短并在一个批处理中

在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁 事务运行时间越长 其持有排它锁或更新锁的时间也就越长 从而堵塞了其它活动并可能导致死锁 保持事务在一个批处理中 可以最小化事务的网络通信往返量 减少完成事务可能的延迟并释放锁

使用低隔离级别

确定事务是否能在更低的隔离级别上运行 执行提交读允许事务读取另一个事务已读取(未修改)的数据 而不必等待第一个事务完成 使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间 从而降低了锁定争夺

使用绑定连接

使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作 次级连接所获得的任何锁可以象由主连接获得的锁那样持有 反之亦然 因此不会相互阻塞

下面有一些对死锁发生的一些建议

)对于频繁使用的表使用集簇化的索引;

)设法避免一次性影响大量记录的T SQL语句 特别是INSERT和UPDATE语句;

)设法让UPDATE和DELETE语句使用索引;

)使用嵌套事务时 避免提交和回退冲突;

lishixin/Article/program/SQLServer/201311/22281

⑹ “sql”加锁机制是什么

您好!锁是数据库中的一个非常重要的概念,它主要用于多用户环境下保证数据库完整性和一致性。x0dx0a 我们知道,多个用户能够同时操纵同一个数据库中的数据,会发生数据不一致现象。即如果没有锁定且多个用户同时访问一个数据库,则当他们的事务同时使用相同的数据时可能会发生问题。这些问题包括:丢失更新、脏读、不可重复读和幻觉读。数据库加锁就是为了解决以上的问题。x0dx0a 当然,加锁固然好,但是一定要避免死锁的出现。x0dx0a 在数据库系统中,死锁是指多个用户(进程)分别锁定了一个资源,并又试图请求锁定对方已经锁定的资源,这就产生了一个锁定请求环,导致多个用户(进程)都处于等待对方释放所锁定资源的状态。这种死锁是最典型的死锁形式, 例如在同一时间内有两个事务A和B,事务A有两个操作:锁定表part和请求访问表supplier;事务B也有两个操作:锁定表supplier和请求访问表part。结果,事务A和事务B之间发生了死锁。死锁的第二种情况是,当在一个数据库中时,有若干个长时间运行的事务执行并行的裤拆操作,当查询分析器处理一种非常复杂的查询例如连接查询时,那么由于不能控制处理的顺序,有可能发生死锁现象。x0dx0a 在应用程序中就可以采用下面的一些方法来尽量避胡棚枣免死锁了: (1)合理安排表访问顺序。 (2)在事务中尽量避免用户干预,尽量使一个事务处理的任务少些, 保持事务简短并在一个批处理中。 (3)数据访问时域离散法, 数据访问时域离散法是指在客户机/服务器结构中,采取各种控制手段控制对数据库或数据库中的和手对象访问时间段。主要通过以下方式实现: 合理安排后台事务的执行时间,采用工作流对后台事务进行统一管理。工作流在管理任务时,一方面限制同一类任务的线程数(往往限制为1个),防止资源过多占用; 另一方面合理安排不同任务执行时序、时间,尽量避免多个后台任务同时执行,另外, 避免在前台交易高峰时间运行后台任务。 (4)数据存储空间离散法。数据存储空间离散法是指采取各种手段,将逻辑上在一个表中的数据分散到若干离散的空间上去,以便改善对表的访问性能。主要通过以下方法实现: 第一,将大表按行或列分解为若干小表; 第二,按不同的用户群分解。 (5)使用尽可能低的隔离性级别。隔离性级别是指为保证数据库数据的完整性和一致性而使多用户事务隔离的程度,SQL92定义了4种隔离性级别:未提交读、提交读、可重复读和可串行。如果选择过高的隔离性级别,如可串行,虽然系统可以因实现更好隔离性而更大程度上保证数据的完整性和一致性,但各事务间冲突而死锁的机会大大增加,大大影响了系统性能。 (6)使用绑定连接, 绑定连接允许两个或多个事务连接共享事务和锁,而且任何一个事务连接要申请锁如同另外一个事务要申请锁一样,因此可以允许这些事务共享数据而不会有加锁的冲突。 x0dx0a 总之,了解SQL Server的锁机制,掌握数据库锁定方法, 对一个合格的DBA来说是很重要的。

⑺ GBase 8s中如何避免死锁问答

锁的问题(锁等待、死锁)大部分由于没有正确理解锁的管理、使用方式导致。要解决锁的问题,我们可以从如下几个方面进行尝试。
1.数据库锁资源的设置
ONCONFIG 参数 LOCKS 设置即为数据库配置数量合适的锁。通过搜索 online 日志是否有动态增加锁的情况:grep 'dynamically allocated' online.log为表设置合理的锁模式、行级锁或者页级锁。
2.应用程序锁使用模式设置
通过 onstat -g sql 查看 SESSION 使用的锁等待模式和隔离级别,要为不同的应用设置合理的隔离级别和锁等待模式。如在应用程序加入如下语句:
set isolation to dirty read;
set lock mode to wait 5;
3.应用程序避免使用不必要的锁
许多应用程序的 SQL 语句由于没有正确地使用索引 INDEX,导致对整个表进行了锁定,导致并发遇到锁的问题。需要通过 SQL 优化和创建合理的索引,避免顺序扫描带来的锁冲突问题。
由于顺序扫描导致的锁等待问题:创建表 test_lock,采用行级锁,并在 c1 字段上创建索引,插入 3 行测试数据。
create table test_lock (c1 int,c2 int,c3 char(10)) lock mode row;
create index idx_test_lock on test_lock(c1);
insert into test_lock values(1,1,'abc');
insert into test_lock values(2,2,'abc');
insert into test_lock values(3,3,'abc');
Session 1 通过索引扫描来修改第 2 行记录:
Begin work;
update test_lock set c3='new' where c1=2;
此时,Session 1 将在第二行记录上加 X 锁。
Session 2 通过 c2 字段以顺序扫描的方式来修改第 3 行记录。
Begin work;
update test_lock set c3='new' where c2=3;
由于 Session 2 采用顺序扫描方式,在扫描过程中发现第 2 行记录加上了 X 锁,故提示锁冲突错误:
244: Could not do a physical-order read to fetch next row.
107: ISAM error: record is locked.
如果在 c2 字段上加上索引,或者通过 c1=3 修改第三行记录,那么就不会出现锁的问题。
4.应用程序优化避免过长占用锁
锁是一个公共资源,需要尽快释放锁资源,减少等待,如果每个事务都可以在微秒级别完成,那么锁就不会是我们关注的问题,减少锁占用的时间也是解决锁问题的关键。我们可以通过避免大事务,把大事务拆分多次提交,这样锁就能尽快释放。另外,优化应用的处理性能,如合理使用索引等,都可以加快事务的处理速度,可无形中解决锁冲突的可能性。
5.设置合理的隔离级别
不同的隔离级别对锁的使用不一样,设置合理的隔离级别,可以提高应用程序的并发性,提高系统的处理能力,减少锁的冲突情况,避免读操作带来的锁问题。

⑻ 如何处理SQL Server死锁问题

死锁,简而言之,两个或者多个trans,同时请求对方正在请求的某个对象,导致双方互相等待。简单的例子如下:x0dx0a trans1 trans2x0dx0a ------------------------------------------------------------------------x0dx0a 1.IDBConnection.BeginTransaction 1.IDBConnection.BeginTransactionx0dx0a 2.update table A 2.update table Bx0dx0a 3.update table B 3.update table Ax0dx0a 4.IDBConnection.Commit 4.IDBConnection.Commit x0dx0a 那么,很容易看到,如果trans1和trans2,分别到达了step3,那么trans1会请求对于B的X锁,trans2会请求对于A的X锁,而二者的锁在step2上已经被对方分别持有了。由于得不到锁,后面的Commit无法执行,这样双方开始死锁。x0dx0a 好,我们看一个简单的例子,来解释一下,应该如何解决死锁问题。x0dx0a -- Batch #1x0dx0a CREATE DATABASE deadlocktestx0dx0a GOx0dx0a USE deadlocktestx0dx0a SET NOCOUNT ONx0dx0a DBCC TRACEON (1222, -1)x0dx0a -- 在SQL2005中,增加了一个新的dbcc参数,就是1222,原来在2000下,我们知道,可以执行dbcc x0dx0a --traceon(1204,3605,-1)看到所有的死锁信息。SqlServer 2005中,对于1204进行了增强,这就是1222。x0dx0a GO x0dx0a x0dx0a IF OBJECT_ID ('t1') IS NOT NULL DROP TABLE t1x0dx0a IF OBJECT_ID ('p1') IS NOT NULL DROP PROC p1x0dx0a IF OBJECT_ID ('p2') IS NOT NULL DROP PROC p2x0dx0a GOx0dx0a CREATE TABLE t1 (c1 int, c2 int, c3 int, c4 char(5000)) x0dx0a GOx0dx0a DECLARE @x intx0dx0a SET @x = 1x0dx0a WHILE (@x <= 1000) BEGINx0dx0a INSERT INTO t1 VALUES (@x*2, @x*2, @x*2, @x*2)x0dx0a SET @x = @x + 1x0dx0a ENDx0dx0a GOx0dx0a CREATE CLUSTERED INDEX cidx ON t1 (c1)x0dx0a CREATE NONCLUSTERED INDEX idx1 ON t1 (c2)x0dx0a GOx0dx0a CREATE PROC p1 @p1 int AS SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1x0dx0a GOx0dx0a CREATE PROC p2 @p1 int ASx0dx0a UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1x0dx0a UPDATE t1 SET c2 = c2-1 WHERE c1 = @p1x0dx0a GOx0dx0a 上述sql创建一个deadlock的示范数据库,插入了1000条数据,并在表t1上建立了c1列的聚集索引,和c2列的非聚集索引。另外创建了两个sp,分别是从t1中select数据和update数据。 x0dx0a 好,打开一个新的查询窗口,我们开始执行下面的query:x0dx0a -- Batch #2x0dx0a USE deadlocktestx0dx0a SET NOCOUNT ONx0dx0a WHILE (1=1) EXEC p2 4x0dx0a GOx0dx0a 开始执行后,然后我们打开第三个查询窗口,执行下面的query:x0dx0a -- Batch #3x0dx0a USE deadlocktestx0dx0a SET NOCOUNT ONx0dx0a CREATE TABLE #t1 (c2 int, c3 int)x0dx0a GOx0dx0a WHILE (1=1) BEGINx0dx0a INSERT INTO #t1 EXEC p1 4x0dx0a TRUNCATE TABLE #t1x0dx0a ENDx0dx0a GOx0dx0a 开始执行,哈哈,很快,我们看到了这样的错误信息:x0dx0a Msg 1205, Level 13, State 51, Procere p1, Line 4x0dx0a Transaction (Process ID 54) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.x0dx0a spid54发现了死锁。 x0dx0a 那么,我们该如何解决它?x0dx0a 在SqlServer 2005中,我们可以这么做:x0dx0a 1.在trans3的窗口中,选择EXEC p1 4,然后right click,看到了菜单了吗?选择Analyse Query in Database Engine Tuning Advisor。x0dx0a 2.注意右面的窗口中,wordload有三个选择:负载文件、表、查询语句,因为我们选择了查询语句的方式,所以就不需要修改这个radio option了。x0dx0a 3.点左上角的Start Analysis按钮x0dx0a 4.抽根烟,回来后看结果吧!出现了一个分析结果窗口,其中,在Index Recommendations中,我们发现了一条信息:大意是,在表t1上增加一个非聚集索引索引:t2+t1。x0dx0a 5.在当前窗口的上方菜单上,选择Action菜单,选择Apply Recommendations,系统会自动创建这个索引。x0dx0a 重新运行batch #3,呵呵,死锁没有了。x0dx0a 这种方式,我们可以解决大部分的Sql Server死锁问题。那么,发生这个死锁的根本原因是什么呢?为什么增加一个non clustered index,问题就解决了呢? 这次,我们分析一下,为什么会死锁呢?再回顾一下两个sp的写法:x0dx0a CREATE PROC p1 @p1 int AS x0dx0a SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1 x0dx0a GOx0dx0a CREATE PROC p2 @p1 int ASx0dx0a UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1x0dx0a UPDATE t1 SET c2 = c2-1 WHERE c1 = @p1x0dx0a GOx0dx0a 很奇怪吧!p1没有insert,没有delete,没有update,只是一个select,p2才是update。这个和我们前面说过的,trans1里面updata A,update B;trans2里面upate B,update A,根本不贴边啊!x0dx0a 那么,什么导致了死锁?x0dx0a 需要从事件日志中,看sql的死锁信息:x0dx0a Spid X is running this query (line 2 of proc [p1], inputbuffer “? EXEC p1 4 ?”): x0dx0a SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1x0dx0a Spid Y is running this query (line 2 of proc [p2], inputbuffer “EXEC p2 4”): x0dx0a UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1x0dx0a x0dx0a The SELECT is waiting for a Shared KEY lock on index t1.cidx. The UPDATE holds a conflicting X lock. x0dx0a The UPDATE is waiting for an eXclusive KEY lock on index t1.idx1. The SELECT holds a conflicting S lock.x0dx0a 首先,我们看看p1的执行计划。怎么看呢?可以执行set statistics profile on,这句就可以了。下面是p1的执行计划x0dx0a SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1x0dx0a |--Nested Loops(Inner Join, OUTER REFERENCES:([Uniq1002], [t1].[c1]))x0dx0a |--Index Seek(OBJECT:([t1].[idx1]), SEEK:([t1].[c2] >= [@p1] AND [t1].[c2] <= [@p1]+(1)) ORDERED FORWARD)x0dx0a |--Clustered Index Seek(OBJECT:([t1].[cidx]), SEEK:([t1].[c1]=[t1].[c1] AND [Uniq1002]=[Uniq1002]) LOOKUP ORDERED FORWARD)x0dx0a 我们看到了一个nested loops,第一行,利用索引t1.c2来进行seek,seek出来的那个rowid,在第二行中,用来通过聚集索引来查找整行的数据。这是什么?就是bookmark lookup啊!为什么?因为我们需要的c2、c3不能完全的被索引t1.c1带出来,所以需要书签查找。 x0dx0a 好,我们接着看p2的执行计划。x0dx0a UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1x0dx0a |--Clustered Index Update(OBJECT:([t1].[cidx]), OBJECT:([t1].[idx1]), SET:([t1].[c2] = [Expr1004]))x0dx0a |--Compute Scalar(DEFINE:([Expr1013]=[Expr1013]))x0dx0a |--Compute Scalar(DEFINE:([Expr1004]=[t1].[c2]+(1), [Expr1013]=CASE WHEN CASE WHEN ...x0dx0a |--Top(ROWCOUNT est 0)x0dx0a |--Clustered Index Seek(OBJECT:([t1].[cidx]), SEEK:([t1].[c1]=[@p1]) ORDERED FORWARD) x0dx0a 通过聚集索引的seek找到了一行,然后开始更新。这里注意的是,update的时候,它会申请一个针对clustered index的X锁的。x0dx0a 实际上到这里,我们就明白了为什么update会对select产生死锁。update的时候,会申请一个针对clustered index的X锁,这样就阻塞住了(注意,不是死锁!)select里面最后的那个clustered index seek。死锁的另一半在哪里呢?注意我们的select语句,c2存在于索引idx1中,c1是一个聚集索引cidx。问题就在这里!我们在p2中更新了c2这个值,所以sqlserver会自动更新包含c2列的非聚集索引:idx1。而idx1在哪里?就在我们刚才的select语句中。而对这个索引列的更改,意味着索引集合的某个行或者某些行,需要重新排列,而重新排列,需要一个X锁。x0dx0a SO???,问题就这样被发现了。x0dx0a 总结一下,就是说,某个query使用非聚集索引来select数据,那么它会在非聚集索引上持有一个S锁。当有一些select的列不在该索引上,它需要根据rowid找到对应的聚集索引的那行,然后找到其他数据。而此时,第二个的查询中,update正在聚集索引上忙乎:定位、加锁、修改等。但因为正在修改的某个列,是另外一个非聚集索引的某个列,所以此时,它需要同时更改那个非聚集索引的信息,这就需要在那个非聚集索引上,加第二个X锁。select开始等待update的X锁,update开始等待select的S锁,死锁,就这样发生鸟。 x0dx0a 那么,为什么我们增加了一个非聚集索引,死锁就消失鸟?我们看一下,按照上文中自动增加的索引之后的执行计划:x0dx0a SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1x0dx0a |--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)x0dx0a 哦,对于clustered index的需求没有了,因为增加的覆盖索引已经足够把所有的信息都select出来。就这么简单。x0dx0a 实际上,在sqlserver 2005中,如果用profiler来抓eventid:1222,那么会出现一个死锁的图,很直观的说。x0dx0a 下面的方法,有助于将死锁减至最少(详细情况,请看SQLServer联机帮助,搜索:将死锁减至最少即可。x0dx0a按同一顺序访问对象。 x0dx0a避免事务中的用户交互。 x0dx0a保持事务简短并处于一个批处理中。 x0dx0a使用较低的隔离级别。 x0dx0a使用基于行版本控制的隔离级别。 x0dx0a将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON,使得已提交读事务使用行版本控制。 x0dx0a使用快照隔离。x0dx0a使用绑定连接。

⑼ SQL频繁的访问一张数据库表,如何避免死锁如何提高性能

事务不能开太多,及时提交,因为事务没有提交时,其他程序是不能对表进行更新操作,降低了数据库的性能。涉及到大量数据的插入和更新是建议使用批量更新的方法。查询提高性能的方法是瞎隐给作为磨磨厅条件的字段加索引,但是变长的汉字最好不要加索引,游改它不能提高查询的效率,最好用联表查询,减少子查询。一个表里的索引不能多于4个,否则插入和更新的速度是很慢的。。。关于数据库还有很多适用的技巧,在此抛砖引玉啦,呵呵