我有一个简单的表 T,它有 Col1、Col2、Col3 和 Col4。列。Col1 是主键。现在我正在运行一些并发代码,
Parallel.For(0, list.Count, new ParallelOptions { MaxDegreeOfParallelism = -1 }, j =>
{
var obj= list[j];
// Do some work
// UPDATE T SET Col2, Col3, Col4 WHERE Col1 =@Col1
col1 的每个值都是不同的。但是当我运行这段代码时,我遇到了太多的死锁。由于 Col1 是 PK(聚集索引),我现在很困惑为什么会这样?
陷入僵局。我看到所有者模式:U
每当我跑步时,
USE master
GO
DROP TABLE temp_sp_who2
GO
CREATE TABLE temp_sp_who2 ( SPID INT, Status VARCHAR(1000) NULL, Login SYSNAME NULL, HostName SYSNAME NULL, BlkBy SYSNAME NULL,DBName SYSNAME NULL,Command VARCHAR(1000) NULL, CPUTime INT NULL,DiskIO BIGINT NULL, LastBatch VARCHAR(1000) NULL, ProgramName VARCHAR(1000) NULL,SPID2 INT , RequestId INT NULL )
GO
INSERT INTO temp_sp_who2
EXEC sp_who2
SELECT BlkBy ,count(*) FROM
temp_sp_who2 WHERE CAST(BlkBy AS NVARCHAR(MAX)) <> CAST(SPID AS NVARCHAR(MAX))
GROUP By BlkBy
ORDER BY Count(*) DESC
GO
我看到 BlkBy 与高数字不同。但是当应用程序运行时,BlkBy 总是很高。
没有死锁痕迹,我们只能根据症状进行猜测。
长期阻塞和死锁表明单例行更新查询正在执行完整扫描,而不是通过 PK 索引定位行。如果主键参数数据类型与引用的列不同,并且导致无法有效使用 PK 索引的非 sargable 表达式,则会发生这种情况。
在应用程序代码或存储过程中声明参数时注意细节很重要,以确保参数定义与引用的列类型匹配。ADO.NET 中类型不匹配(不一定是您的类型)的常见原因是
AddWithValue
方法,它根据提供的值推断 SQL 类型。.NET 中的字符串是 Unicode,因此字符串值的结果参数类型是nvarchar
. 将 nvarchar 参数与旧的 SQL 排序规则 varchar 列进行比较会阻止使用主键索引来定位行,从而导致完全扫描,从而导致活动系统上的阻塞和死锁。