【问题标题】:SQL-Server Transaction Blocking MysterySQL-Server 事务阻塞之谜
【发布时间】:2017-12-28 17:20:10
【问题描述】:

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

那么问题来了:

我们设置的服务器与我被告知的规格完全相同(尽管我很怀疑 :))。一个用于开发工作;另一个用于客户端测试。在我们的开发环境中,处理运行没有问题;但是在客户端测试站点上,它每次都挂起。我正在拔头发试图确定原因。

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

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

  • 将参数选择替换为方法选择,同时保留对参数的插入。(与参数选择完全相同的列定义)
  • 将@newKey 替换为现有表单的有效整数。
  • 删除了 Param 选择的“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

【问题讨论】:

  • “FormDB 上有锁”是什么意思?
  • 嗨@Alex。从我使用 SQL Profiler、Activity Monitor 和来自Microsoft 的以下存储过程中获得的详细信息中,我可以看到一个 SPID(状态处于睡眠状态并等待命令)正在阻止另一个 SPID 上的当前插入(状态暂停)。

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


【解决方案1】:

我仍然不明白这个问题的原因,但我找到了解决方案。

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

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

问题已解决 - 但如果有人能解释为什么会发生这种情况,我将不胜感激。谢谢!

【讨论】:

  • 我的猜测:您可能在某处有未提交的事务。如果您使用链接服务器,请检查“启用分布式事务促进”设置是否相同。
  • 太棒了。感谢@Alex 的提示!
猜你喜欢
  • 2013-08-02
  • 1970-01-01
  • 1970-01-01
  • 2017-11-10
  • 2012-02-10
  • 1970-01-01
  • 2015-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多