SQLSERVER临时表引起的"锁"案

在一个CS结构的项目里使用SQLServer时碰到一个有意思的现象,如下是从日志中摘出来的用户操做:数据库


用户A的操做会引起程序在事务中使用Local临时表,例如:缓存

1 BEGIN TRAN
2 
3 SELECT * INTO #temp FROM DB1.dbo.Table1
4 
5 --do something
6 
7 DROP TABLE #temp
8 
9 COMMIT TRAN

用户B的操做也会引起程序使用临时表且在删除临时表前会先判断临时表的存在性,例如:服务器

1 SELECT * INTO #MyTempTable FROM TestJJV9.dbo.test
2 
3 IF EXISTS(SELECT 1 FROM tempdb.dbo.sysobjects WHERE name like '%#MyTempTable%')
4 BEGIN
5 DROP TABLE tempdb.dbo.#MyTempTable
6 END

用户A和用户B使用的是同一个SQLServer实例,可是用的是不一样的数据库。session

 

那么,现象来了,用户A的操做越频繁,用户B越容易碰到长时间的等待甚至超时。spa

 

分析
从用户B的操做来看,判断临时表存在性的SQL语句(line3)致使了对tempdb.dbo.sysobjects的扫描(sysobjects是一个view,实际上扫描的是sys.sysschobjs的汇集索引),该扫描须要对sys.sysschobjs的汇集索引伸请S锁。
而用户A的操做会对sys.sysschobjs表的汇集索引的KEY持有一个X锁,在用户A的事务提交以前,用户B没法得到S锁,因此处于等待状态。
若是有C,D,E等用户持续在执行相似用户A的操做,那么用户B基本上就只能等死了。。。日志

解决方案
使用OBJECT_ID判断临时表的存在性,能够避免对sys.sysschobjs的扫描,防止被锁(猜测其内部多是从相似缓存的结构中取得的结果),例如:code

SELECT * INTO #MyTempTable FROM DB2.dbo.AnyTable

IF OBJECT_ID('tempdb.dbo.#MyTempTable') IS NOT NULL    
BEGIN
DROP TABLE tempdb.dbo.#MyTempTable
END

 

另外,上述用户A的操做不管生成的是Local临时表仍是Global临时表,都有这个现象,缘由是同样的。blog

最后转载一个即时调查当前服务器情况的SP,要调查相似上述问题时,能够以下使用:索引

exec sp_WhoIsActive @get_locks = 1, @find_block_leaders = 1

从blocking_session_id, blocked_session_count, locks列能够看出谁正在被谁阻塞事务

相关文章
相关标签/搜索