SQL Server锁-避免插入重复条目

SQL Server锁-避免插入重复条目,sql,sql-server,Sql,Sql Server,在阅读了大量与上述主题相关的文章和答案后,我仍然想知道SQL Server数据库引擎在以下示例中是如何工作的: 假设我们有一个名为t3的表: create table t3 (a int , b int); create index test on t3 (a); 以及以下查询: INSERT INTO T3 SELECT -86,-86 WHERE NOT EXISTS (SELECT 1 FROM t3 where t3.a=-86); 在基于列“a”验证行不存在后,查询在表t3中插入一

在阅读了大量与上述主题相关的文章和答案后,我仍然想知道SQL Server数据库引擎在以下示例中是如何工作的:

假设我们有一个名为t3的表:

create table t3 (a int , b int);
create index test on t3 (a);
以及以下查询:

INSERT INTO T3
SELECT -86,-86
WHERE NOT EXISTS (SELECT 1 FROM t3 where t3.a=-86);
在基于列“a”验证行不存在后,查询在表t3中插入一行

许多文章和答案表明,使用上述查询不可能将一行插入两次

为了执行上述查询,我假设数据库引擎的工作方式如下:

INSERT INTO T3
SELECT -86,-86
WHERE NOT EXISTS (SELECT 1 FROM t3 where t3.a=-86);
  • 首先执行子查询
  • 数据库引擎在某个范围上设置共享锁
  • 数据被读取
  • 共享锁被释放。根据MSDN a共享 一旦数据丢失,锁就会被释放 已经读过了
  • 如果一行不存在,它将在表中插入新行
  • 新行使用独占锁(x)锁定
  • 现在考虑下面的场景:

  • 上述查询由处理器A(spid1)执行
  • 同一查询由 处理器B(SPID2)
  • [SPID 1]数据库引擎设置共享锁
  • [SPID 1]子查询读取 数据。现在返回行
  • [SPID 1]共享锁是 释放
  • [SPID 2]数据库引擎设置 共享(s)锁
  • [SPID 2]子查询读取 数据。不返回任何行
  • [SPID 2]共享锁是 释放
  • 这两个过程都会进行行插入(我们会得到一个重复的条目)
  • 我错过什么了吗?上述方法是避免重复条目的正确方法吗

    避免重复条目的安全方法是使用下面的代码,但我只是想知道上面的方法是否正确

    begin tran
        if (SELECT 1 FROM t3 with (updlock) where t3.a=-86)
        begin
            INSERT INTO T3
            SELECT -86,-86
        end
    commit
    

    如果列上只有一个唯一的约束,则永远不会有重复的约束

    您概述的技术将避免在(第二次“同时”)操作失败的情况下捕获错误或异常


    我想补充一点,依靠“外部”代码(甚至T-SQL)来增强数据库的一致性不是一个好主意。在所有情况下,在表级别使用声明性引用完整性对于数据库确保一致性和匹配期望都很重要,无论应用程序代码编写得是否良好。与安全性一样,您需要利用深度防御策略—约束、唯一索引、触发器、存储过程和视图都有助于采取多层方法,以确保数据库为应用程序或系统提供一致和可靠的接口。

    要在多条语句之间保持锁定,它们必须包装在事务中。在您的示例中:

    If (SELECT 1 FROM t3 with (updlock) where t3.a=-86)
        INSERT INTO T3 SELECT -86,-86
    
    可以在执行插入之前释放更新锁。这将可靠地工作:

    begin transaction
    If (SELECT 1 FROM t3 with (updlock) where t3.a=-86)
        INSERT INTO T3 SELECT -86,-86
    commit transaction
    
    单个语句始终包装在事务中,因此这也会起作用:

     INSERT INTO T3 SELECT -86,-86
     WHERE NOT EXISTS (SELECT 1 FROM t3 with (updlock) where t3.a=-86)
    

    (这是假设您关闭了“隐式事务”,如默认的SQL Server设置。)

    是的,唯一的约束/索引是首选的,也是唯一可靠的防止重复的方法。在某些情况下,其他任何事情都注定要失败。这是正确的。这两个语句必须包含在一个事务中。我更正了问题中的陈述。那么你是说插入到。。。如果不强制执行更新锁,则(…)语法将不起作用。这就是我所知道的,但在读了这么多文章,甚至没有提到这一点之后,我开始怀疑它是否真的需要。谢谢你的澄清。