单个 SQL Server 语句是否具有原子性和一致性?

2023-11-23

是SQL Server中的一条语句ACID?

我的意思是

给定单个 T-SQL 语句,未包装在BEGIN TRANSACTION / COMMIT TRANSACTION,是该语句的操作:

  • Atomic:要么执行所有数据修改,要么不执行任何数据修改。
  • 持续的:事务完成后,必须使所有数据保持一致状态。
  • Isolated:并发事务所做的修改必须与任何其他并发事务所做的修改隔离。
  • Durable:交易完成后,其影响将永久保留在系统中。

我问的原因

我在实时系统中有一条语句似乎违反了查询规则。

实际上我的 T-SQL 语句是:

--If there are any slots available, 
--then find the earliest unbooked transaction and mark it booked
UPDATE Transactions
SET Booked = 1
WHERE TransactionID = (
   SELECT TOP 1 TransactionID
   FROM Slots
      INNER JOIN Transactions t2
      ON Slots.SlotDate = t2.TransactionDate
   WHERE t2.Booked = 0 --only book it if it's currently unbooked
   AND Slots.Available > 0 --only book it if there's empty slots
   ORDER BY t2.CreatedDate)

Note:但一个更简单的概念变体可能是:

--Give away one gift, as long as we haven't given away five
UPDATE Gifts
SET GivenAway = 1
WHERE GiftID = (
   SELECT TOP 1 GiftID
   FROM Gifts
   WHERE g2.GivenAway = 0
   AND (SELECT COUNT(*) FROM Gifts g2 WHERE g2.GivenAway = 1) < 5
   ORDER BY g2.GiftValue DESC
)

在这两个语句中,请注意它们是单个语句 (UPDATE...SET...WHERE).

存在错误交易的情况"booked";它实际上是在选择一个later交易。盯着这个看了16个小时后,我被难住了。就好像 SQL Server 完全违反了规则一样。

我想知道如果结果如何Slots视图在更新发生之前发生变化?如果 SQL Server 没有持有怎么办SHARED锁定在交易在那date?是否有可能单个语句不一致?

所以我决定测试一下

我决定检查子查询或内部操作的结果是否不一致。我创建了一个简单的表,其中包含一个int column:

CREATE TABLE CountingNumbers (
   Value int PRIMARY KEY NOT NULL
)

从多个连接,在一个紧密的循环中,我称之为单个 T-SQL 语句:

INSERT INTO CountingNumbers (Value)
SELECT ISNULL(MAX(Value), 0)+1 FROM CountingNumbers

换句话说,伪代码是:

while (true)
{
    ADOConnection.Execute(sql);
}

几秒钟之内我得到:

Violation of PRIMARY KEY constraint 'PK__Counting__07D9BBC343D61337'. 
Cannot insert duplicate key in object 'dbo.CountingNumbers'. 
The duplicate value is (1332)

语句是原子的吗?

单个语句不是原子的这一事实让我想知道单个语句是否是原子的?

或者还有更多subtle的定义陈述,这与(例如)SQL Server 认为的语句不同:

enter image description here

这是否从根本上意味着在单个 T-SQL 语句的范围内,SQL Server 语句不是原子的?

如果单个语句是原子的,那么是什么导致了密钥违规?

从存储过程中

而不是远程客户端打开n连接,我用存​​储过程尝试了它:

CREATE procedure [dbo].[DoCountNumbers] AS

SET NOCOUNT ON;

DECLARE @bumpedCount int
SET @bumpedCount = 0

WHILE (@bumpedCount < 500) --safety valve
BEGIN
SET @bumpedCount = @bumpedCount+1;

PRINT 'Running bump '+CAST(@bumpedCount AS varchar(50))

INSERT INTO CountingNumbers (Value)
SELECT ISNULL(MAX(Value), 0)+1 FROM CountingNumbers

IF (@bumpedCount >= 500)
BEGIN
    PRINT 'WARNING: Bumping safety limit of 500 bumps reached'
END
END

PRINT 'Done bumping process'

在 SSMS 中打开 5 个选项卡,在每个选项卡中按 F5,然后观察它们是否也违反了 ACID:

Running bump 414
Msg 2627, Level 14, State 1, Procedure DoCountNumbers, Line 14
Violation of PRIMARY KEY constraint 'PK_CountingNumbers'. 
Cannot insert duplicate key in object 'dbo.CountingNumbers'. 
The duplicate key value is (4414).
The statement has been terminated.

因此,该故障与 ADO、ADO.net 无关,或者与以上都无关。

15 年来,我一直假设 SQL Server 中的单个语句是一致的;以及唯一的

事务隔离级别 xxx 怎么样?

对于要执行的 SQL 批处理的不同变体:

  • 默认(读已提交):关键违规

    INSERT INTO CountingNumbers (Value)
    SELECT ISNULL(MAX(Value), 0)+1 FROM CountingNumbers
    
  • 默认(读已提交),显式事务无错误按键违规

    BEGIN TRANSACTION
    INSERT INTO CountingNumbers (Value)
    SELECT ISNULL(MAX(Value), 0)+1 FROM CountingNumbers
    COMMIT TRANSACTION
    
  • 可序列化: 僵局

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    BEGIN TRANSACTION
    INSERT INTO CountingNumbers (Value)
    SELECT ISNULL(MAX(Value), 0)+1 FROM CountingNumbers
    COMMIT TRANSACTION
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    
  • snapshot(更改数据库以启用快照隔离后):密钥违规

    SET TRANSACTION ISOLATION LEVEL SNAPSHOT
    BEGIN TRANSACTION
    INSERT INTO CountingNumbers (Value)
    SELECT ISNULL(MAX(Value), 0)+1 FROM CountingNumbers
    COMMIT TRANSACTION
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    

Bonus

  • 微软 SQL Server 2008 R2 (SP2) - 10.50.4000.0 (X64)
  • 默认事务隔离级别(READ COMMITTED)

结果我写的每一个查询都被破坏了

这肯定会改变事情。我写过的每一个更新语句都从根本上被破坏了。例如。:

--Update the user with their last invoice date
UPDATE Users 
SET LastInvoiceDate = (SELECT MAX(InvoiceDate) FROM Invoices WHERE Invoices.uid = Users.uid)

值错误;因为可以在之后插入另一张发票MAX和之前UPDATE。或者来自 BOL 的示例:

UPDATE Sales.SalesPerson
SET SalesYTD = SalesYTD + 
    (SELECT SUM(so.SubTotal) 
     FROM Sales.SalesOrderHeader AS so
     WHERE so.OrderDate = (SELECT MAX(OrderDate)
                           FROM Sales.SalesOrderHeader AS so2
                           WHERE so2.SalesPersonID = so.SalesPersonID)
     AND Sales.SalesPerson.BusinessEntityID = so.SalesPersonID
     GROUP BY so.SalesPersonID);

没有独占锁,SalesYTD是错的。

这些年我怎么能做点什么。


我一直在假设 SQL Server 中的单个语句是一致的情况下进行操作

这个假设是错误的。以下两个事务具有相同的锁定语义:

STATEMENT

BEGIN TRAN; STATEMENT; COMMIT

完全没有区别。单个语句和自动提交不会改变任何内容。

因此,将所有逻辑合并到一个语句中并没有帮助(如果有的话,那也是偶然的,因为计划发生了变化)。

让我们解决手头的问题。SERIALIZABLE将修复您所看到的不一致,因为它保证您的事务的行为就像单线程执行一样。同样,他们的行为就好像他们立即执行了一样。

你将会陷入僵局。如果您同意重试循环,那么此时您就完成了。

如果您想投入更多时间,请应用锁定提示来强制对相关数据进行独占访问:

UPDATE Gifts  -- U-locked anyway
SET GivenAway = 1
WHERE GiftID = (
   SELECT TOP 1 GiftID
   FROM Gifts WITH (UPDLOCK, HOLDLOCK) --this normally just S-locks.
   WHERE g2.GivenAway = 0
    AND (SELECT COUNT(*) FROM Gifts g2 WITH (UPDLOCK, HOLDLOCK) WHERE g2.GivenAway = 1) < 5
   ORDER BY g2.GiftValue DESC
)

您现在将看到并发性减少。根据您的负载,这可能完全没问题。

您的问题的本质使得实现并发变得困难。如果您需要解决方案,我们需要应用更具侵入性的技术。

您可以稍微简化一下更新:

WITH g AS (
   SELECT TOP 1 Gifts.*
   FROM Gifts
   WHERE g2.GivenAway = 0
    AND (SELECT COUNT(*) FROM Gifts g2 WITH (UPDLOCK, HOLDLOCK) WHERE g2.GivenAway = 1) < 5
   ORDER BY g2.GiftValue DESC
)
UPDATE g  -- U-locked anyway
SET GivenAway = 1

这消除了一个不必要的连接。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

单个 SQL Server 语句是否具有原子性和一致性? 的相关文章

  • 计算2个日期之间每个日期的记录数

    我必须创建一个查询来返回多轴图表的结果 我需要计算为 2 个日期之间的每个日期创建的 ID 数量 我试过这个 DECLARE StartDate datetime2 7 11 1 2020 EndDate datetime2 7 2 22
  • 需要在 SQL Server 中透视字符串值

    我有一个包含值的表 描述为 Occupation String Name String Developer A Developer B Designer X Coder Y Coder Z 我需要数据透视格式的值 Designer Deve
  • 当附加触发器时,Linq-to-sql 插入和更新失败

    我最近在 linq to sql 方面遇到了一些问题 问题在于 当我们将触发器附加到事件时 它 认为 插入和更新失败 一个例子可以是一行 当一行被更改时 附加一个触发器将 LastUpdated 冒号设置为当前时间 这将导致 linq to
  • SQL存储过程执行时间差异

    我在 win form 应用程序中遇到奇怪的问题 我正在调用一个存储过程 并且执行大约需要 6 秒 此存储过程接受多个参数 包括一个输出参数 从应用程序级别我使用 Dim dt1 DateTime Now cmd ExecuteNonQue
  • SQL Server:如果存在会大大减慢查询速度

    正在使用SQL Server 2012 我找到了一些关于查询优化的主题 并将 EXISTS 与 COUNT 进行比较 但我找不到这个确切的问题 我有一个看起来像这样的查询 select from tblAccount as acc join
  • 如何从 Laravel 执行存储过程

    我需要在表单提交数据后执行存储过程 我让存储过程按照我想要的方式工作 并且我的表单正常工作 我只是不知道从 laravel 5 执行 sp 的语句 它应该是这样的 执行 my stored procedure 但我似乎在网上找不到类似的东西
  • 如何在Word 2010中从SQL数据库检索数据?

    我想用 MS SQL 数据库中的数据填充 Word 文档 这可能吗 如果可能的话 如何实现 我过去曾通过多种方式做到这一点 这取决于用户是从 Microsoft Word 外部还是从 Microsoft Word 内部启动操作 From I
  • 仅选择 Varchar 列中的数字[重复]

    这个问题在这里已经有答案了 在 SQL Server 2008 R2 中 我在 varchar 12 列中有一些数据 它看起来像这样 Data 1234 1765 34566 123 SDRMH HJG434 我想从所有包含 的行中删除 并
  • 一张表中按最大日期过滤重复行的 SQL 查询

    我有一个 SQL 数据库 persons 它具有基于 IDNum 列的重复条目 我需要查询条目并仅根据最新创建日期显示行或重复条目 这是查询 SELECT IDNum PersonPGUID CreatedDateTime FirstNam
  • SQL Server:当列为 NTEXT 时 IN('asd') 不起作用

    我该如何解决这个问题 where someNtext IN asd asd1 给出错误 消息 402 第 16 层 状态 1 第 XXXXX 行数据类型 ntext 和 varchar 在等于运算符中不兼容 An INlist 只是 OR
  • 如何在审计触发器中使用system_user但仍使用连接池?

    我想做以下两件事 在我的数据库表上使用审计触发器来识别哪个用户更新了什么 使用连接池来提高性能 对于 1 我在数据库触发器中使用 system user 来识别进行更改的用户 但这阻止我执行需要通用连接字符串的 2 有没有一种方法可以让我充
  • SQL Server“未找到网络路径”在不同环境中随机且不频繁地发生

    类似 如果不是同一个问题 随机遇到网络路径未找到异常 https stackoverflow com questions 38696448 network path not found exception encountered rando
  • 如果存在多个相同的 ID,则使用 where 子句

    我有下表 ID source Name Age 1 SQL John 18 2 SAP Mike 21 2 SQL Mike 20 3 SAP Jill 25 我希望每个ID都有一条记录 这背后的想法是 如果 ID 仅出现一次 无论来源如何
  • SQL Server 中的每个实体自动增量字段?

    我的数据库中有一个稳定的 食物 主键为 fooD 我有第二个表 foo Attributes 其外键引用 foo fooD 我想在 fooAttributes 表上有一个组合键 fooID attributeNumber 当我插入新属性时
  • 查询获取每条记录的最小日期[重复]

    这个问题在这里已经有答案了 我想获取表中每条记录的最小日期 该表具有多个带有一个主键的日期条目 看看我的桌子 CaseNo Entry date ABC 001 2 12 13 ABC 002 2 09 13 ABC 001 1 01 13
  • 当两个表非常相似时,什么时候应该将它们合并?

    我有事件和照片 然后对两者进行评论 现在 我有两个评论表 一个用于与事件相关的评论 另一个用于照片评论 架构与此类似 CREATE TABLE EventComments CommentId int EventId int Comment
  • 什么是多维 OLAP CUBE 并给出超过 3 维的多维数据集示例

    由于我是 SSAS 的新手 一直在阅读有关多维 OLAP 多维数据集的文章 并努力理解多维数据集的概念 据说虽然术语 多维数据集 表示三个维度 但多维数据集最多可以有 64 个维度 你能解释一下这在立方体上怎么可能吗 除了 3 Dim 示例
  • SQL Server - SQL 替换整个数据库中所有表中的所有列

    这是一个很遥远的事情 我猜这个问题没有简单的答案 但是 我继承了一个数据库 其中填充了一些可怕的数据 许多包含描述的行都有回车符 这意味着当我们 BCP 输出数据时 它会带有回车符 我的问题 有没有办法在 MS SQL Server 中对整
  • INTEGER 到 DATETIME 的转换与 VB6 不同

    我正在查看一些遗留的 VB6 代码 比我的时代早很多年 它对 SQL 2005 数据库运行查询 它提供了日期限制WHERE子句 其中日期作为整数值给出CLng VB6 中的日期 e g WHERE SomeDateField gt 4006
  • 如何重置 SQL Server 中表的 IDENTITY 列? [复制]

    这个问题在这里已经有答案了 我怎样才能重置我的IDENTITY我已经填充的表中的列 我尝试过类似的方法 但它不起作用 WITH TBL AS SELECT ROW NUMBER OVER ORDER BY profile id AS RN

随机推荐