UNIQUE 索引键违规

squ*_*man 6 sql-server-2008-r2 locking unique-constraint

我有一个带有 PK 和唯一非聚集索引的表,如下所示:

CREATE TABLE Table1
(
    Id INT IDENTITY(1,1) NOT NULL,
    Field1 VARCHAR(25) NOT NULL,
    Field2 VARCHAR(25) NULL,
 CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED (Id ASC)
)

CREATE UNIQUE NONCLUSTERED INDEX IX_Field1_Field2 ON Table1
(
    Field1 ASC,
    Field2 ASC
)
WITH
    (
        PAD_INDEX = OFF,
        STATISTICS_NORECOMPUTE = OFF,
        SORT_IN_TEMPDB = OFF,
        IGNORE_DUP_KEY = OFF,
        DROP_EXISTING = OFF,
        ONLINE = OFF,
        ALLOW_ROW_LOCKS = ON,
        ALLOW_PAGE_LOCKS = ON
    )
Run Code Online (Sandbox Code Playgroud)

我有 2 个工作,我发现它们的执行时间相互重叠。它们都将相同的内容包含在INSERT此表中,并且最后开始的作业经常失败,因为它尝试将记录插入到Table1具有重复索引键值的中。

INSERT Table1
SELECT Field1, Field2
FROM SomeOtherTable sot WITH (NOLOCK)
WHERE NOT EXISTS (
    SELECT 1
    FROM Table1 t1
    WHERE sot.Field1 = t1.Field1
    AND sot.Field2 = t1.Field2
)
Run Code Online (Sandbox Code Playgroud)

据我INSERT所知,当对NOT EXISTSfrom Job2 进行评估时,in Job1 仍在执行,导致 Job2 尝试插入重复的键值。在我看来,锁定Table1没有按预期发生。

我不知道为什么会这样。这与 中使用的NOLOCK提示有什么关系INSERT吗?我不认为该提示会包含Table1在其范围内,只有SomeOtherTable.

我知道我可以通过为索引设置IGNORE_DUP_KEY为来减轻重复键错误,ON在这种情况下这对我们来说没问题。不过,我想知道为什么重复项会出现在第二个 INSERT 中。

小智 2

如果您想完全理解 SQL Server 中锁定和隔离级别的所有复杂性,需要掌握很多知识,并且您的问题本质上引导您朝这个方向发展。

所有信息都可以在http://msdn.microsoft.com/en-us/library/ms173763.aspx上找到,尽管它并不容易阅读。更简单的阅读可以在http://databases.about.com/od/sqlserver/a/isolationmodels.htm上找到- 请特别注意“幻读”部分。

对于您想要执行的操作,您需要两个事务都以“SERIALIZABLE”隔离级别运行。这基本上意味着它们将“好像”一个在另一个之后运行——它们实际上是序列化的。

发生的情况是,两个事务都决定需要插入相同的记录,然后两个事务都尝试插入相同的记录。测试逻辑所采用的锁与插入所采用的锁是分开的,这使得这种情况发生。SERIALIZABLE 将阻止这种情况发生,因为它的“其他事务无法插入其键值落在当前事务中任何语句读取的键范围内的新行,直到当前事务完成”子句。另一种思考方式是,需要保留测试中的读锁,直到整个事务完成。这可以使用 HOLDLOCK 查询提示来指定;但我不得不说,我个人并没有使用 HOLDLOCK,而只是使用“SET TRANSACTION ISOLATION LEVEL SERIALIZABLE”版本。