SQL并发删除锁定

时间:2015-11-30 14:31:24

标签: sql-server entity-framework concurrency transactions

一些背景资料:
通过删除旧数据并插入新数据来定期更新数据。 数据被聚集到配置文件中,我将其用作主键的一部分并删除旧数据 只有一个进程写入数据,因此不必担心其他人的更新冲突 其他过程只读取我计划通过Snapshot-Isolation解决的数据 我通过实体框架6访问数据,代码优先建模。

问题: 我开始收集几个并行的配置文件的数据。适用于具有简单外键关系的所有表,但对于具有自引用的表。

public class Web
{
    public Web()
    {
        CacheDate = DateTime.Now;
    }

    [Key, Column(Order = 0)]
    public Guid ProfileGuid { get; set; }
    [Key, Column(Order = 2)]
    public Guid WebId { get; set; }

    public string Data { get; set; }

    public virtual List<List> Lists { get; set; }
    public virtual List<Web> SubWebs { get; set; }

    public Guid? ParentProfileGuid { get; set; }
    public Guid? ParentWebId { get; set; }

    public virtual Web ParentWeb { get; set;}
}

 modelBuilder.Entity<CacheWeb>()
     .HasMany(e => e.SubWebs)
     .WithOptional(e => e.ParentWeb)
     .HasForeignKey(e => new { e.ParentProfileGuid, e.ParentWebId })
     .WillCascadeOnDelete(false);

由于性能问题,我不使用EF删除数据,而是使用自定义sql命令:InsertContext.Database.ExecuteSqlCommand(String.Format("DELETE FROM Webs WHERE ProfileGuid = '{0}'", profile.Guid));

如果我在并发事务中调用其中两个删除,则第一个调用会创建一个锁,以防止第二个事务继续。

据我所知,锁定以某种方式连接到自引用的索引。

我刚发现另一个奇怪的行为,它取决于首先删除哪个事务。 我能找到的数据的唯一区别是亲子关系的深度 如果我首先运行事务A(有一个父母和几个直接子女)我之后不能运行事务B(有一些父 - >孩子 - >子女关系),但如果我先运行B,我可以在之后运行A遇到现有锁的问题。

有没有办法解决这个问题?或者任何想法可能是锁的问题? 如果您需要更多信息,请评论您需要什么,因为我不确定什么是重要的。

编辑:澄清

BEGIN Transaction t1
DELETE FROM Webs WHERE ProfileGuid = 'b35dbba4-54fc-4df7-b1c8-e559d81dfee3'
BEGIN Transaction t2
DELETE FROM Webs WHERE ProfileGuid = 'b35dbba4-54fc-4df7-b1c8-e559d81dfee4'

的工作原理。交换订单,t1必须等待t2完成。

EDIT2:执行计划 Seek Scan

EDIT3:模型和完整查询 model_s 执行计划来自第3个查询(删除CacheList)

BEGIN Transaction t1
DELETE FROM CacheItems WHERE ProfileGuid = 'B35DBBA4-54FC-4DF7-B1C8-E559D81DFEE3'
DELETE FROM CacheFolders WHERE ProfileGuid = 'B35DBBA4-54FC-4DF7-B1C8-E559D81DFEE3'
DELETE FROM CacheLists WHERE ProfileGuid = 'B35DBBA4-54FC-4DF7-B1C8-E559D81DFEE3'
DELETE FROM CacheWebs WHERE ProfileGuid = 'B35DBBA4-54FC-4DF7-B1C8-E559D81DFEE3'

2 个答案:

答案 0 :(得分:0)

SQL Server可以根据每个特定查询锁定不同的级别。这可以解释为什么更改订单可以使交易锁定另一个或不锁定。

避免锁定的方法是

这两种解决方案都很危险,因为较低的隔离级别可能会导致各种错误。

答案 1 :(得分:0)

关键是在事务之间实现完全隔离。也就是说,两个事务不需要触及相同的记录。

将记录划分为并行化组时,共享id的记录应位于同一组中。一个简单的Parallel.Foreach是不够的。可以按顺序处理每个组内的数据,而无需与另一个并行组共享任何数据。

除了应用程序中的逻辑隔离外,您还需要确保在索引节点级别将不同的查询彼此隔离。确保所有查询都使用索引查找而不是扫描。这将避免两个查询在搜索记录时将S锁(共享锁)放在同一记录上,然后在尝试删除时无法将其转换为X锁。使用“显示实际执行计划”运行每个查询,并确保没有看到索引扫描。如果您看到扫描并且不明白为什么然后发表评论。