为什么我在 INSERT 上遇到快照隔离问题?

jos*_*der 11 sql-server snapshot-isolation

给定两张表

家长

KeyID   GroupID   Name  Active
Run Code Online (Sandbox Code Playgroud)

孩子

KeyID   ParentID  Name
Run Code Online (Sandbox Code Playgroud)

Child.ParentID 是 FKed Parent.KeyID

我们将Parent和插入Child到单个事务中。

如果在事务处于活动状态Parent时更新了不同的行(例如Active1 -> 0),则会Child INSERT失败:

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

据我所知,为什么我会收到“由于更新冲突而中止快照隔离事务”?这可能是由于完全扫描以验证外键。

事实上,删除外键确实允许Child INSERT按预期完成。

话虽如此,Child表上外键上的非聚集索引似乎并没有帮助解决这个问题,所以我有点不知所措。

我们为此数据库打开了 RCSI,并且事务在快照隔离模式下运行。

额外细节

我发现当插入到 Child 的行数大于给定的行数时会出现此问题。此时查询优化器从 a 切换Nested Loops (Left Semi Join)到 a Merge Join (Left Semi Join)

抱歉没有包括为单个父记录插入多个子记录的事实。

工作插入(20 个子记录): 工作刀片

插入失败(50 条子记录): 插入失败

插入sproc大致是这样的:

KeyID   GroupID   Name  Active
Run Code Online (Sandbox Code Playgroud)

并发更新将类似于:

KeyID   ParentID  Name
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 7

OPTION (LOOP JOIN)INSERT语句中添加提示。

或者使用计划指南(或查询存储)强制嵌套循环半连接计划形状。

你可能会发现这OPTION (FAST 1)也有效。

关键是要避免合并半连接,其中许多(可能是所有)引用表的行都被当前事务触及。如果遇到任何具有更改(包括创建)的父行,则会引发更新冲突错误