【问题标题】:How are my SQL Server constraints being bypassed?我的 SQL Server 约束是如何被绕过的?
【发布时间】:2021-05-20 19:40:14
【问题描述】:

我们在数据库中发现了一些违反活动约束的行。这怎么可能?

约束处于活动状态,因为我们不能手动添加绕过此约束的行。然而,当我们运行CHECKCONSTRAINTS(Files) 时,我们发现它在我们的测试运行中被绕过了几次。有问题的行都是在半秒内创建的,这表明存在某种竞争条件。

这是应用于表的约束。该规则旨在确保给定父文件夹中的名称唯一性:

ALTER TABLE Files ADD CONSTRAINT UniqueNameInParentFolder CHECK
    CheckUniqueNameInFolder(ParentFoldersID, Name) = 1;

此约束调用如下所示的函数:

-- first check for the new name in the Folders table
IF ((SELECT COUNT(*) FROM Folders 
     WHERE ParentFoldersID = @FoldersID AND Name = @Name) = 0)
BEGIN 
    -- then check for it in the Files table
    IF ((SELECT COUNT(*) FROM Files 
         WHERE ParentFoldersID = @FoldersID AND Name = @Name) <= 1)
        RETURN 1
END
RETURN 0

单个行被添加到事务中,所以我很难理解重复行是如何绕过这个限制的。

【问题讨论】:

  • 是的,我们对Folders 有类似的限制,在那里也看到了类似数量的违规行为。我们目前正在使用READ_COMMITTED_SNAPSHOT

标签: sql-server sql-server-2008


【解决方案1】:

基于 UDF 的检查约束是垃圾。正如您所发现的那样,并发、RBAR、隔离等。一些链接:

在这种情况下,SQL Server 最安全的方法是使用标准约束,例如唯一键和外键。不过,我不明白您为什么要检查文件夹表以了解对文件表的约束。

为了防止文件和文件夹在给定的父文件夹中具有相同名称​​仅,请使用索引视图。重复文件或重复文件夹需要表级唯一性。

CREATE VIEW CheckUnique
WITH SCHEMABINDING
AS
SELECT fo.ParentFoldersID, fo.Name
FROM
   Folders fo
   JOIN
   File fi ON fo.ParentFoldersID  = fi.ParentFoldersID AND fo.Name = fi.Name
GO
CREATE UNIQUE CLUSTERED INDEX IXCU_CheckUnique ON CheckUnique (ParentFoldersID, Name)
GO

或者一个触发器。

从不检查约束中的 UDF

【讨论】:

  • 在我们的例子中,同名的文件夹和文件可能不存在于同一个父文件夹中,所以很遗憾我认为 unique/fk 约束是不够的。
【解决方案2】:

CheckUniqueNameInFolder 函数几乎不检查任何内容。在该约束检查下可以添加大量重复项。它有两个按顺序运行的不同 SELECTS(因此第一个选择检查的条件在第二个运行时可能会失效),并且在任何情况下,约束最多只能告诉 strong> 检查发生时没有重复,绝不会告诉 在插入/更新发生时没有重复。由于检查不会锁定在 U 或 X 模式下验证的键,因此可以同时发生多个插入,进行检查,找到没有重复的,然后全部继续插入 相同的进入。

正确执行唯一约束的唯一方法是使用唯一约束

使用“文件”的完整路径创建一个计算列,并使用 UNIQUE 约束在整个路径上强制执行唯一性,或者可能对 (ParentFolderID, Name) 使用 UNIQUE 约束。不要单独存储文件夹和文件,为文件夹和文件(例如条目)使用一个公共表,因为它们占用 same 命名空间。

【讨论】:

  • 是的,出于某种原因,我们假设事务可以处理我们看到的问题,但您说得对,没有锁定。 (在这一点上,合并表格对我们来说是一个相当大的重新设计,但仍然是一个有趣的想法。)
【解决方案3】:

在默认read committed 级别的事务中运行此操作将在负载下失败。

读取不是互斥的和序列化的,因此两个并发事务都可以读取该行不存在。您可以将UPDLOCK,ROWLOCK,HOLDLOCK 提示添加到SELECT

【讨论】:

    猜你喜欢
    • 2014-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-04
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 2011-12-29
    相关资源
    最近更新 更多