我有一个C#应用程序,它使用存储过程将数据插入SQL Server(2008)表。我正在使用多线程来做到这一点。正在从线程内部调用存储过程。 现在我的存储过程在插入数据时使用“tablock”。 执行此代码时,我收到以下错误: “事务(进程ID)在锁资源上与另一个进程陷入僵局,并被选为死锁受害者。重新运行该事务。”
任何人都可以帮我解决这个问题吗?
答案 0 :(得分:13)
当两个Sql Server进程访问相同的资源但以不同的顺序访问时,会发生这种情况。因此他们最终都在等待另一个进程,这是一个僵局。
有很多方法可以阻止它,包括:
with (nolock)
锁定提示进行查询。E.g。如果Proc1锁定table1然后table2,但Proc2锁定table2然后table1,则可能出现问题。您可以重写proc以同一顺序获取锁定以避免此问题。
答案 1 :(得分:4)
您可以将查询封装在TRY CATCH块中,并捕获错误编号(与锁相关)
然后你可以自动重试,达到一定的数量。所以你会做类似以下的事情;
DECLARE @RetryNo Int = 1
,@RetryMaxNo Int = 5;
WHILE @RetryNo < @RetryMaxNo
BEGIN
BEGIN TRY
-- put your query that generates locks here....
SELECT @RetryNo = @RetryMaxNo;
END TRY
BEGIN CATCH
IF ERROR_NUMBER() IN (1204, 1205, 1222)
BEGIN
SET @RetryNo += 1;
-- it will wait for 10 seconds to do another attempt
WAITFOR DELAY '00:00:10';
END
ELSE
THROW;
END CATCH
END
您还可以使用表格提示,例如UPDLOCK。
答案 2 :(得分:0)
您可以使用Lock对象
static object _lock = new object();
public static void _main()
{
lock (_lock)
{
_bulkcopy(myData);
}
}
public static void _bulkcopy(DataTable dt)
{
try
{
using (var connection = new SqlConnection(ConfigurationSettings.AppSettings.Get("DBConnection")))
{
connection.Open();
SqlTransaction transaction = connection.BeginTransaction();
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction))
{
bulkCopy.BatchSize = 100;
bulkCopy.DestinationTableName = "dbo.MyTable";
try
{
bulkCopy.WriteToServer(dt);
}
catch (Exception)
{
transaction.Rollback();
connection.Close();
}
}
transaction.Commit();
}
}
catch { }
}
答案 3 :(得分:0)
请确保您要更新或插入哪个字段,该字段具有非聚集索引。如果不可用,您可以先在此表上创建此字段的非聚集索引,然后按照以下步骤进行创建。
右键单击表并选择属性。
在属性的右侧面板中选择“选项”。
在“锁定”选项卡中,“允许页面锁定”设置为“ False”,“允许行锁定”必须为“ True”,然后按确定。
答案 4 :(得分:0)
这是S Kumar Dubey提供的MSDN解决方案
执行SP:SP_LOCK在结果中,您将获得SPID,DBID,OBJID,INDID, TYPE,RESOURCE,MODE,STATUS现在检查状态列(如果是) 显示等待,然后杀死该SPID。杀死特定的SPID执行 SP:杀死65(其中65是SPID)
看来您需要成为SQL Server管理员才能解决此问题。