【问题标题】:SQL Concurrent delete lockingSQL并发删除锁定
【发布时间】:2015-11-30 14:31:23
【问题描述】:

一些背景信息:
通过删除旧数据并插入新数据来定期更新数据。 数据聚集到配置文件中,我将其用作主键的一部分并用于删除旧数据。
只有一个进程写入数据,因此不必担心其他人的更新冲突。
其他进程仅读取我计划通过快照隔离解决的数据。
我通过实体框架 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:执行计划

EDIT3:模型和完整查询 执行计划来自第 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'

【问题讨论】:

  • 您能否使用 TSQL_Locks 模板运行 SQL Server 跟踪并发布死锁图?这将显示死锁的确切细节
  • 我试过了,它没有向死锁事件文件写入任何内容。大概是因为没有死锁吧? (以前从未使用过该图)。一旦我提交一项事务,另一项事务就会顺利运行。但由于一项交易可能需要长达一个小时的等待时间,因此无法选择。
  • 您会看到一系列事件,例如 Lock:Deadlock Chain, Deadlock Graph。
  • 删除查询似乎与计划无关。您可以添加表结构以及非聚集索引吗?然后我可以重新创建场景。
  • 我的错,错误现在出现在另一个查询中,我将添加完整的表结构和新查询

标签: sql-server entity-framework concurrency transactions


【解决方案1】:

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

避免锁的方法是

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

【讨论】:

    【解决方案2】:

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

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

    除了应用程序中的这种逻辑隔离之外,您还需要确保不同的查询在索引节点级别相互隔离。确保所有查询都使用索引搜索而不是扫描。这将避免两个查询在搜索记录时将 S 锁(共享锁)放在同一记录上,然后在尝试删除时无法将其转换为 X 锁。使用 Show Actual Execution Plan 运行您的每个查询,并确保您没有看到任何索引扫描。如果您看到扫描结果但不明白为什么,请发表评论。

    【讨论】:

    • 谢谢,索引扫描似乎是问题所在。其中一个查询使用索引扫描或索引搜索,具体取决于我要删除的配置文件。我添加了执行计划,也许你可以看看并给我一个建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-20
    相关资源
    最近更新 更多