繁体   English   中英

事务在锁资源上死锁

[英]Transaction was deadlocked on lock resources

我有这个 class 允许在可序列化事务中通过 Dapper 向数据库发送更新请求:

    public async Task<int> UpdateTable(UpdateModel model)
    {
        await using var connection = createConnection();
        await connection.OpenAsync();
        await using var tx = connection.BeginTransaction(IsolationLevel.Serializable);
        var rowsCount = await connection.ExecuteAsync(@$"

            UPDATE Table WITH (UPDLOCK, SERIALIZABLE)
            SET
                Date = @Date,
                Attempts = Attempts + 1
            WHERE
                Id = @Id
                AND DATEDIFF(MINUTE, Date, GETDATE()) < 60",
            new
            {
                model.Id,
                model.Date
            }, commandTimeout: connection.CommandTimeout, transaction: tx);
        tx.Commit();
        return rowsCount;
    }

我发送 10 个异步请求并打印结果如下:

var count = 10;

while (true)
{
    var tasks = new List<Task>();
    var providerId = Guid.NewGuid();
    for (var i = 0; i < count; i++)
        tasks.Add(upsert(providerId));

    var result = Task.WhenAll(tasks.ToArray());

    result.Wait();
    Thread.Sleep(5000);
    Console.WriteLine("------------------------------------------------------------------");
}

async Task upsert(Guid id)
{
    var response = await client.Upsert(new UpsertModel
    {
        Id = id,
        Date = DateTime.Now
    });

    Console.WriteLine($"RowsCount: {response.Content}, Id: {id}");
}

当我发送 10 个异步请求时,我收到来自 10 个请求的 9 个死锁。消息看起来像这样:事务(进程 ID 67)在锁资源上与另一个进程死锁,并被选为死锁受害者。 重新运行事务。

为什么我会收到死锁? 我需要做什么才能避免死锁?

在我的回答中,我假设您使用的是 SQL 服务器,因为UPDLOCK是 SQL 服务器特定的关键字。

让我们参考表格提示文档

有一个很大的红色警告:

注意力

由于 SQL 服务器查询优化器通常会为查询选择最佳执行计划,因此我们建议只有经验丰富的开发人员和数据库管理员才将提示用作最后的手段

如果UPDLOCK有充分的理由这样做,它将对整个表设置一个独占锁。 由于您构建查询以更新不同的行,因此您可以使用ROWLOCK提示,前提是您不使用单个 UPDATE 指令更新数万行。

正如 Micorsoft 页面所说,处理您的问题的更简单方法是不使用表格提示。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM