【问题标题】:C# SQL Transaction (Process ID 72) was deadlockedC# SQL 事务(进程 ID 72)被死锁
【发布时间】:2017-07-18 19:14:06
【问题描述】:

我在 Visual Studio 中创建了一个可供多个用户访问的 WinForm。 每当用户访问数据时,我想锁定数据,以便其他用户由于某些业务逻辑而无法同时查看相同的数据。

下面是我的c#代码

using (SqlConnection conn = new SqlConnection(connectString))
{
    using (SqlCommand cmd=new SqlCommand("get_First_Unscan_Record_withUpdate",conn))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@usrName", username_Form);
        conn.Open();
        record_number = cmd.ExecuteScalar().ToString();
    }
}

我的 SQL 过程

    CREATE PROCEDURE [dbo].[get_First_QC_Record_withUpdate]
@usrName nvarchar(50)
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

Declare @srno int

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

select top 1 @srno=srno from [KYC].[dbo].[KYC_Index] (XLOCK) where  [qc]='N' and [qc_int]='N' and transform_int='Y' and transform_status='Y' order by srno

update KYC_Index set [qc_int]='Y',[qc_int_by]=@usrName,[qc_int_time]=GETDATE(),@srno=KYC_Index.srno where srno = @srno

select @srno

我的用户仍然遇到死锁错误

{“事务(进程 ID 72)在锁资源上与另一个进程死锁,已被选为死锁牺牲品。重新运行事务。”}

我做错了什么?还是我应该改变?请指导。

【问题讨论】:

  • 这不是你实现队列的方式。有一个update top (1) 和rowlock, holdlock, readpast,有它output 的id,并有一个涵盖您过滤的所有字段的索引。
  • 你不会因为 one 东西而陷入僵局 - 你会因为 two 而陷入僵局。你已经有一个XLOCK(我个人会选择UPDLOCK),所以它不能只是一个试图成为更新锁的读锁。所以:你这里有交易吗?您的代码中没有一个,但可能有一个TransactionScope 可以在您的上下文/SPID 中添加其他锁……有吗?有点感觉你可以在一个 SQL 操作中完成所有这些操作,不过……也许通过OUTPUT 子句来获取srno?不过,我首先要尝试的是使用UPDLOCK。然后我会尝试重写为单个 SQL 操作。
  • @GSerg 你是在建议这样的事情吗?使用 (UPDLOCK,HOLDLOCK) 从 KYC_Index t1 更新 t1 集 [qc_int]='Y',[qc_int_by]=@usrName,[qc_int_time]=GETDATE(),@srno=t1.srno 其中 t1.srno = (select top 1 t2.srno from KYC_Index t2 where t2.[qc]='N' and t2.[qc_int]='N' and t2.transform_int='Y' and t2.transform_status='Y' order by t2.srno)跨度>
  • 不,这仍然是两个伪装成一个查询。
  • @GSerg 我们需要按 SRNO 顺序更新数据。那就是两个查询。您能否在上述情况下为我指明正确的方向。

标签: c# sql-server visual-studio


【解决方案1】:

您无法通过锁定数据来隐藏数据。要对其他用户隐藏,同一个表中必须有hidden 之类的列,或者在hidden_rows 之类的外部链接表中更好。 SERIALIZABLE 级别也不是必需的。你只需要 BEGIN TRAN / COMMIT TRAN 或者你可以从 C# 端做同样的事情(begin_tran - try { do_work, commit_tran } catch { rollback })。

https://msdn.microsoft.com/en-us/library/86773566(v=vs.110).aspx

在SELECT 语句中使用(UPDLOCK) 提示 - 它将锁定该行以进行写入,直到事务结束,其他人可以读取。

【讨论】:

  • 据我所知,这里的意图是让qc_int / qc_int_by / qc_int_time 成为您的hidden 专栏 - 它正在记录:它被“锁定”,b:谁,和c:何时;回复UPDLOCK - 代码使用的是XLOCK,应该涵盖以及更多,不是吗?
  • @MarcGravell “UPDLOCK 不会阻止并发 SELECT 或 INSERT,但会阻止任何 UPDATE 或 DELETE” - 我的建议是不要锁定该行以进行读取(选择)。我认为隐藏不是必需的,但作者认为它将保护数据免受并发编辑。问题是通常 UI 会显示记录,并且可以同时显示给 2 个以上的用户。那么这种隐藏将无济于事 - 两个用户都可以编辑和保存更改。锁定/隐藏仅在单击“保存”/“确定”按钮时发生。
  • PS: 使用hidden 栏,软件可以在UI加载编辑时隐藏记录,编辑完成后“显示”。
  • @i486 名字不重要;称它为 hidden 不会改变它的工作方式 - qc_int 工作方式相同
  • @i486 重新锁定:你不能只谈论它是否阻止选择;您需要添加指定锁的上下文。所以是的,虽然 UPDLOCK 不会阻止 常规 选择,但它绝对会阻止另一个选择 指定 UPDLOCK,因为 U/U=完整锁兼容性矩阵中的 C。此外,updlock 可以 阻止insert,if 选择需要键范围锁定(因为:可序列化)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多