You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF6批量导入ERP数据时SQL死锁问题求助

解决批量导入中标记Deleted字段时的SQL死锁问题

死锁原因分析

死锁发生在更新Deleted字段的语句上,核心原因是并发批次之间的锁竞争:

  • 当前导入逻辑中,GetBatchFromDb查询现有工单时使用默认的共享锁(读提交隔离级别下),锁会持有到事务提交(即SaveChanges执行完成)。
  • 当多个并发批次同时处理同一批工单时,每个批次都先以共享锁读取相同的行,之后尝试将共享锁升级为排他锁以更新Deleted字段,此时就会形成互相等待的死锁,最终SQL Server选择其中一个批次作为受害者终止。

解决方案

1. 查询时获取更新锁(UPDLOCK)

在查询现有工单时直接获取更新锁,避免后续升级锁时的冲突。更新锁允许其他事务读取数据,但阻止其他事务获取更新锁或排他锁,确保当前事务拥有优先更新的权限。

修改GetBatchFromDb方法中的查询逻辑(以EF Core为例):

private static async Task<Dictionary<string, WorkOrder>> GetBatchFromDb(DatabaseEntities db, SemaphoreSlim dbLimiter, List<DataEntryStruct> batch)
{
    await dbLimiter.WaitAsync();
    try
    {
        var objIds = batch.Select(b => b.ObjID).ToList();
        return await db.WorkOrders
            .Where(w => objIds.Contains(w.ObjID))
            .With(LockBehavior.UpdLock) // 添加更新锁提示
            .ToDictionaryAsync(w => w.ObjID);
    }
    finally
    {
        dbLimiter.Release();
    }
}

如果是EF6,可通过原生SQL实现:

private static Task<Dictionary<string, WorkOrder>> GetBatchFromDb(DatabaseEntities db, SemaphoreSlim dbLimiter, List<DataEntryStruct> batch)
{
    dbLimiter.Wait();
    try
    {
        var objIds = batch.Select(b => b.ObjID).ToArray();
        var workOrders = db.WorkOrders
            .SqlQuery("SELECT * FROM WorkOrders WHERE ObjID IN (@p0) WITH (UPDLOCK)", objIds)
            .ToList();
        return Task.FromResult(workOrders.ToDictionary(w => w.ObjID));
    }
    finally
    {
        dbLimiter.Release();
    }
}

2. 启用数据库快照隔离

开启SQL Server的READ_COMMITTED_SNAPSHOT选项,让读提交隔离级别下的查询使用版本化数据,不再持有共享锁,从根源避免读锁和写锁的冲突:

ALTER DATABASE YourDatabaseName SET READ_COMMITTED_SNAPSHOT ON;

此操作需要数据库处于单用户模式,执行前需确保没有其他连接。

3. 优化批次划分逻辑

确保同一工单不会被多个并发批次重复处理,比如按ObjID的范围或哈希值划分批次,减少跨批次的锁竞争概率。

4. 缩小事务范围

当前逻辑中,从查询到保存变更的整个过程都在同一个DbContext实例中,锁的持有时间较长。可以考虑将查询和变更操作拆分,或者在完成所有数据准备后再创建DbContext执行更新,缩短锁的持有时间。

验证建议

  • 调整后重新运行导入程序,观察SQL Server Profiler是否还有死锁事件。
  • 若仍出现死锁,可启用SQL Server的死锁图形捕获(通过Profiler或Extended Events),进一步分析死锁的资源和参与进程,定位是否有其他外部操作干扰。

内容的提问来源于stack exchange,提问作者Tibso

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 00:55:26