为什么我收到“由于更新冲突而中止快照隔离事务”?

Mar*_*ark 9 sql-server snapshot-isolation

我们有两张桌子

  1. 父级(Id int 身份、日期日期时间、名称 nvarchar)
  2. 孩子(Id int 身份、ParentId int、日期日期时间、名称 nvarchar)

Child 与 Parent 有外键关系。

我们启用了数据库级读提交快照隔离。

我们只为父和子插入和删除行(无更新)

我们有一个进程(事务)从 Child(然后是 Parent)中删除旧数据

我们有多个其他进程(事务)将新数据插入父(然后是子)

删除过程会定期(但不是所有时间)回滚,即使插入过程没有插入新的子行,这些子行引用删除要删除的父行 - 它只是创建新的父行和一个或多个引用新父级的新子行

删除父行时的错误是:

由于更新冲突,快照隔离事务中止。您不能使用快照隔离直接或间接访问数据库 'Test' 中的表 'dbo.Child' 以更新、删除或插入已被另一个事务修改或删除的行。重试事务或更改更新/删除语句的隔离级别。

我知道人们建议在外键列上建立索引 - 我们不希望在理想情况下(出于空间/性能原因)这样做 - 除非这是使其工作的唯一可靠方法。

注意到这一点:https : //stackoverflow.com/questions/10718668/snapshot-isolation-transaction-aborted-due-to-update-conflict

和相当不错的文章:https : //sqlperformance.com/2014/06/sql-performance/the-snapshot-isolation-level

但这些都没有给我想要的理解:)

Pau*_*ite 17

从父表中删除时,SQL Server 必须检查是否存在引用该行的任何 FK 子行。当没有合适的子索引时,这个检查会执行子表的全盘扫描:

完整的儿童扫描

如果扫描遇到自删除命令的快照事务启动以来已被修改的行,则会因更新冲突(根据定义)而失败。完全扫描显然会触及表格中的每一行。

使用合适的索引,SQL Server 可以定位和测试子表中可以匹配要删除的父表的行。当这些特定行未被修改时,不会发生更新冲突:

儿童寻找

请注意,行版本控制隔离级别下的外键检查采用共享锁(为了正确性)以及检测更新冲突。例如,上面关于子表访问的内部提示是:

PhyOp_Range TBL: [dbo].[Child]
    提示(READ-COMMITTEDLOCK FORCEDINDEX DETECT-SNAPSHOT-CONFLICT)

遗憾的是,这目前没有在执行计划中公开。

我的相关文章: