SQL-Server事务阻塞之谜

时间:2017-07-22 19:17:06

标签: stored-procedures sql-server-2012 transactions blocking

我有一个用于表单处理的.Net应用程序,可以删除/更新/插入三个不同的SQL Server 2012数据库中的数据。当应用程序运行时,它会打开一个数据上下文,然后在该上下文中为每个需要处理的表单打开一个事务(这会每分钟运行一次,因此通常一次只能运行一个表单)。在这个事务中发生了一堆事情 - 包括多个存储过程调用。

所以这就是问题所在:

我们设置的服务器与我所说的是完全相同的规格(虽然我可疑:))。一个用于开发工作;另一个用于客户端测试。在我们的开发环境中,处理运行没有问题;但在客户端测试网站上,它每次都会挂起。而我正在试着确定原因。

在以下TSQL代码中,插入Param表失败。除列名外,Param表与Method表基本相同。两个插入都具有与Form表类似的外键关系,并且都将int值插入到ID列中。

当我运行SQL Server Profiler时,我被告知FormDB上有一个不允许插入的锁。但是,我可以更改Param插入的select语句,它可以工作。我已经通过以下方式进行了更改,所有这些方式都“起作用”,因为它们不会导致阻塞问题:

  • 使用Method select替换Param select,同时将插入保持为Param。(与param select完全相同的列defs)
  • 将@newKey替换为现有表单的有效整数。
  • 删除了Param select的“from”部分并硬编码了paramID的单个int值(即选择@ newKey,1,@ modifyDate,@ modifyUser)

我觉得我正在失去理智,因为我无法理解为什么它不起作用。当组合的select语句中有三个东西 - @ newKey,ParamID和from语句时,插入似乎只会失败。

我确保每个sproc都有SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED并在必要时使用nolock。

为什么我可以通过上面的三个场景成功插入Param表,但是后面的代码中的Param插入失败了?为什么我在探查器中没有收到相同的锁定消息?此过程中大约有5个其他插入符合相同的模式。所有这些都没有问题。

有什么想法吗?感谢。

USE [PROD]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[HELPSPROC]
    @oldKey int 
AS

BEGIN
    SET NOCOUNT ON;
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

    declare @newKey int
    , @spKey int
    , @modifyDate datetime = getdate()
    , @modifyUser varchar(30) = 'User'

/*
a bunch of stuff happens here, including setting the @spKey value. 
this all happening correctly -- we have a valid integer value when we go into the next part
*/

---------------FORM---------------
INSERT INTO FormDB.dbo.Form
    (formtype, formstatus, modifydate, modifyuser)
Select 
    'TestFormType', 'DRAFT', @modifyDate, @modifyUser                            
From FormDB2.dbo.Form f  
Where f.Pkey = @oldKey

--grab the new int identifier -- works                     
set @newKey = (select scope_identity())

/*
stuff happens here. all is good in this part
*/


---------------Param---------------
INSERT INTO  FormDB.dbo.Param
    (FormKey, ParamID, ModifyDate, ModifyUser) 
select @newKey, p.ParamID, @modifyDate, @modifyUser
from PROD.dbo.Table1 apd 
    inner join PROD.dbo.ParameterTable p 
        on apd.TableTwoKey = p.TableTwoKey
where apd.PKey = @spKey



---------------Method---------------
INSERT INTO  FormDB.dbo.Method
    (FormKey, MethodID, ModifyDate, ModifyUser) 
select @newKey, r.MethodID, @modifyDate, @modifyUser
from PROD.dbo.Table1 apd 
    inner join PROD.dbo.MethodTable r 
        on apd.TableTwoKey = r.TableTwoKey
where apd.PKey = @spKey

/*
one more insert ...
*/


RETURN 1

END

GO

1 个答案:

答案 0 :(得分:0)

我仍然不明白为什么这个问题,但我找到了解决方案。

此进程的vb.net代码中发生了很多事情:跨三个独立数据库的多个linq-to-sql插入/删除/更新,以及两个单独的存储过程调用。为了增加这种混淆,为每个数据库声明了单独的上下文,每个上下文都有自己的事务。简而言之,一堆活动部件。

第二个存储过程调用是有条件的,基于处理表单的某些值。我只是从vb.net代码中取出了那个调用,并将条件逻辑和存储过程调用放在第一个proc中。这解决了它。第二个存储过程直接在第一个之后调用 - 待决条件 - 所以一切都很好。

问题解决了 - 但如果有人能解释为什么会这样,我将不胜感激。谢谢!