为什么原子语句需要锁提示?

2023-12-06

Question

对以下语句应用锁有什么好处?

同样,如果我们不包含这些提示,我们会​​看到什么问题?即它们是否可以防止竞争条件、提高性能或其他什么?询问它们可能是为了防止出现一些我没有考虑过的问题,而不是我假设的竞争条件。

注意:这是此处提出的问题的溢出:FIFO 队列的 SQL 线程安全 UPDATE TOP 1

有问题的声明

WITH nextRecordToProcess AS
(
    SELECT TOP(1) Id, StatusId
    FROM    DemoQueue
    WHERE   StatusId = 1 --Ready for processing
    ORDER BY DateSubmitted, Id 
)
UPDATE nextRecordToProcess
SET StatusId = 2 --Processing
OUTPUT Inserted.Id 

要求

  • SQL 用于从队列中检索未处理的记录。
  • 要获取的记录应该是队列中状态为就绪(StatusId = 1)的第一条记录。
  • 可能有多个工作进程/会话处理来自该队列的消息。
  • 我们希望确保队列中的每个记录仅被拾取一次(即由单个工作人员),并且每个工作人员按照消息在队列中出现的顺序处理消息。
  • 一个工作人员比另一个工作人员工作得更快是可以的(即,如果工作人员 A 获取记录 1,然后工作人员 B 获取记录 2,如果工作人员 B 在工作人员 A 完成处理记录 1 之前完成记录 2 的处理,则可以)。我们只关心获取记录的情况。
  • 没有正在进行的交易;即我们只想从队列中取出记录;在我们回来处理状态之前,我们不需要将其保持锁定状态Processing to Processed.

上下文的附加 SQL:

CREATE TABLE Statuses
(
    Id SMALLINT NOT NULL PRIMARY KEY CLUSTERED
    , Name NVARCHAR(32) NOT NULL UNIQUE
)
GO
INSERT Statuses (Id, Name)
VALUES (0,'Draft')
, (1,'Ready')
, (2,'Processing')
, (3,'Processed')
, (4,'Error')
GO
CREATE TABLE DemoQueue
(
    Id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , StatusId SMALLINT NOT NULL FOREIGN KEY REFERENCES Statuses(Id)
    , DateSubmitted DATETIME --will be null for all records with status 'Draft'
)
GO

建议声明

在讨论队列的各种博客中,以及引起此讨论的问题中,建议将上述语句更改为包含锁定提示,如下所示:

WITH nextRecordToProcess AS
(
    SELECT TOP(1) Id, StatusId
    FROM    DemoQueue WITH (UPDLOCK, ROWLOCK, READPAST)
    WHERE   StatusId = 1 --Ready for processing
    ORDER BY DateSubmitted, Id 
)
UPDATE nextRecordToProcess
SET StatusId = 2 --Processing
OUTPUT Inserted.Id 

我的理解

据我了解,如果需要锁定,这些提示的好处是:

  • UPDLOCK:因为我们选择记录来更新其状态,所以我们需要确保在我们读取该记录之后但在更新之前读取该记录的任何其他会话将无法读取该记录更新它(或者更确切地说,这样的语句必须等到我们执行更新并释放锁后,其他会话才能看到我们的记录及其新值)。
  • ROWLOCK:当我们锁定记录时,我们希望确保我们的锁只影响我们锁定的行;即,因为我们不需要锁定许多资源/我们不想影响其他进程/我们希望其他会话能够读取队列中的下一个可用项目,即使该项目与我们锁定的记录位于同一页面中。
  • READPAST:如果另一个会话已经从队列中读取一个项目,我们的会话应该选择队列中的下一个可用(未锁定)记录,而不是等待该会话释放其锁定。

也就是说,如果我们运行下面的代码,我认为这是有意义的:

DECLARE @nextRecordToProcess BIGINT

BEGIN TRANSACTION

SELECT TOP (1) @nextRecordToProcess = Id
FROM    DemoQueue WITH (UPDLOCK, ROWLOCK, READPAST)
WHERE   StatusId = 1 --Ready for processing
ORDER BY DateSubmitted, Id 

--and then in a separate statement

UPDATE DemoQueue
SET StatusId = 2 --Processing
WHERE Id = @nextRecordToProcess

COMMIT TRANSACTION

--@nextRecordToProcess is then returned either as an out parameter or by including a `select @nextRecordToProcess Id`

然而,当选择和更新发生在同一个语句中时,我会假设没有其他会话可以在我们的会话的读取和更新之间读取相同的记录;所以不需要显式的锁定提示。

我是否从根本上误解了锁的工作原理?或者这些提示的建议是否与其他一些类似但不同的用例相关?


John 是对的,因为这些是优化,但在 SQL 世界中,这些优化可能意味着“快速”与“难以忍受的数据大小慢”之间的差异和/或“有效”与“无法使用的死锁混乱”之间的差异。

读过去的提示很清楚。对于另外两个,我觉得我需要添加更多背景信息:

  • ROWLOCK 提示是为了防止页锁粒度扫描。锁定粒度(行与页)是在查询开始时预先确定的,并且基于对查询将扫描的页数的估计(第三种粒度,表,仅在特殊情况下使用,不适用于此处) )。通常,出队操作不必扫描如此多的页面,以便引擎考虑页面粒度。但我见过引擎决定使用页面锁定粒度的“野外”情况,这会导致出列中的阻塞和死锁
  • 需要UPDLOCK来防止升级锁死锁的情况。 UPDATE 语句在逻辑上分为搜索需要更新的行,然后更新这些行。搜索需要锁定它评估的行。如果该行符合条件(满足 WHERE 条件),则该行将被更新,并且更新始终是独占锁。那么问题是如何在搜索过程中锁定行?如果您使用共享锁,那么两个 UPDATE 将查看同一行(它们可以,因为共享锁允许它们),都决定该行是否合格,并且都尝试将锁升级为独占 -> 死锁。如果您在搜索期间使用排它锁,则不会发生死锁,但是 UPDATE 将在与任何其他读取评估的所有行上发生冲突,即使该行不符合条件(更不用说排它锁无法在不破坏的情况下提前释放)两相锁定)。这就是为什么存在 U 模式锁,一种与 Shared 兼容(以便候选行的 UPDATE 求值不会阻塞读取)但与另一个 U 不兼容(以便两个 UPDATE 不会死锁)的原因。基于 CTE 的典型出队需要此提示有两个原因:

    1. 因为是 CTE,所以查询处理并不总是理解 CTE 内的 SELECT 是 UPDATE 的目标,并且应该使用 U 模式锁,并且
    2. 出队操作将始终在相同的行之后进行更新(“出队”的行),因此死锁很常见。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么原子语句需要锁提示? 的相关文章

  • 空间索引无助于 SQL 查询(性能非常慢)

    我正在尝试测试包含 170 万个邮政编码的表中纬度 经度值的空间索引的性能 我创建了一个地理列并向其中添加了一个索引 但是使用空间索引的查询比在同一个表中的纬度 经度列上使用 正常 索引的查询要慢得多 至少 100 倍 但是查询计划显示索引
  • 使用触发器找出哪些行被插入、更新或删除

    我在数据库中有一个名为指示的表 它有三列Name Age and Enable 我想创建一个触发器 每当Age未满 18 岁并且Enable是真的 我想在插入的那一刻检查指示表上的记录 这样我就可以检查是否应该在报警时插入 I found
  • 计算日期范围内的天数以及可能重叠的排除集

    给定以下示例查询 考虑到这些范围可能具有重叠的日期 并且还给出了一组要排除的范围 那么计算日期范围内的总天数的合理且高效的方法是什么 更简单地说 我有一个表 其中包含一组关闭计费的日期范围 我从一个日期范围 例如 Jan1 Jan31 开始
  • 子查询总性能与案例总性能

    我必须根据 where 子句对一些列进行求和 以便更好地理解我在这里实现一个临时表 declare tbl table a int b int c int insert into tbl values 1 2 3 insert into t
  • C 中的互斥锁/锁:C11 `mtx_lock()` 与 `pthread_mutex_lock()`

    互斥体直到 C11 才被引入 C 标准 对吗 既然它们已经存在 那么人们应该使用哪一个或更喜欢哪一个 以及何时使用 为什么 有什么区别 C11 s mtx lock vs pthread mutex lock C11 s mtx lock
  • 范围身份与当前身份

    经过大量研究后 我对应该在 sql 中使用哪个身份跟踪器有点困惑 据我了解 scope identity 将为我提供从任何表更新的最后一个 id 而 ident current 将返回指定表中的最后一个 id 因此 考虑到这些信息 在我看来
  • t-sql 中的行相乘

    我有下表 ID Number 1 41 5 2 42 5 3 43 5 2 44 5 2 45 5 1 46 5 1 47 5 我需要编写一个查询 该查询将返回不同的 ID 和相应的 Number 列值相乘 对于给定的表结果应该是这样的 I
  • 确定表的行大小

    如何确定表的最大行大小 我正在寻找一个可以执行此操作的工具或脚本 这样我就不必手动添加每列的大小 我的目标是生成一份太宽表格的报告 以便我们可以考虑重组它们 我知道我们有几个太宽 每个 8K 页面只能容纳 1 行 但我想找到其余的 另一种方
  • 全局变量上的 Linux 定时器

    我在互联网上找到了下面的代码 我试图了解Linux计时器是如何工作的 无论如何 正如你在下面看到的counter1是全局变量 如果while正在处理它并且计时器关闭并改变 会发生什么counter1的值 我需要在那里加锁吗 timertst
  • SQL Server 表不使用默认值

    我正在使用 SSIS 包填充表 这个想法是 每当包上传到表时 它都会使用时间戳记该值getdate 当我打开它时 我的 DDL 看起来像这样 CREATE TABLE REPORTING post ssis table 1 validati
  • 实体框架以错误的顺序插入子对象

    Question 为什么 EF 首先在它所依赖的对象 TimesheetActivity 之前插入具有依赖项的子对象 PersonnelWorkRecord 另外我有哪些纠正这个问题的选择 ERD 简化 This is predefined
  • 用数组“插入”

    我想知道是否有一种方法可以在值列表上使用 插入 我正在尝试这样做 insert into tblMyTable Col1 Col2 Col3 values value1 value2 value3 所以 我想说的是 value2 将是一个字
  • IF EXISTS (SELECT 1...) 与 IF EXISTS (SELECT TOP 1 1...)

    这是一个纯粹的学术问题 这两个陈述实际上是相同的吗 IF EXISTS SELECT TOP 1 1 FROM Table1 SELECT 1 ELSE SELECT 0 Versus IF EXISTS SELECT 1 FROM Tab
  • 在创建 IDENTITY 的同一个 T-SQL 语句中获取 IDENTITY 值?

    有人问我是否可以有一个插入语句 其中有一个作为 身份 列的 ID 字段 以及分配的值是否也可以插入到同一插入语句中同一记录中的另一个字段中 这可能吗 SQL Server 2008r2 Thanks 你不能真正做到这一点 因为将用于的实际值
  • TransactionScope 在某些机器上自动升级到 MSDTC?

    在我们的项目中 我们使用 TransactionScope 来确保我们的数据访问层在事务中执行其操作 我们的目标是not要求在我们的最终用户的计算机上启用 MSDTC 服务 问题是 在我们一半的开发人员机器上 我们可以在禁用 MSDTC 的
  • 在 SQL Server 中创建层次结构

    我有以下格式的数据 Table 1 e id e name e type 1 CBC 2 2 ABC 3 3 N2 1 4 CBC1 3 5 ABC1 3 6 N1 1 Table 2 N ID N Name 3 N2 6 N1 Table
  • 使用 IF..ELSE IF 控制 T-SQL SP 中的流程 - 还有其他方法吗?

    我需要将我的 T SQL 存储过程 MS SQL 2008 控制流分支到多个方向 CREATE PROCEDURE fooBar inputParam INT AS BEGIN IF inputParam 1 BEGIN END ELSE
  • 在 T-SQL 中解析 JSON 数组

    在我们的 SQL Server 表中 我们有一个存储有字符串数组的 json 对象 我想以编程方式将该字符串拆分为几列 但是 我似乎无法让它发挥作用 或者即使有可能 是否可以在WITH子句中创建多个列 或者在select语句中创建多个列是更
  • 表名搜索

    我使用以下命令在特定数据库的存储过程中搜索字符串 USE DBname SELECT Name FROM sys procedures WHERE OBJECT DEFINITION OBJECT ID LIKE xxx 修改上面的内容是否
  • 如何在SSRS 2012中显示基于总金额的前10名

    我只需要显示前 10 名Class基于Total SUM Premium 柱子 我转到类代码属性组 gt 过滤器并按 SUM Net Written Premium 设置前 10 名 但它不起作用 我只需要显示前 10 名 而且总金额也应该

随机推荐

  • 如何在 Tkinter 应用程序中嵌入终端?

    我想在我的 Tkinter 主窗口中嵌入一个终端 我想要一个子窗口 其中可以运行终端 基于 Bash 的终端 我还希望能够让我的程序与终端交互 至少我想读取当前工作目录和 或设置它 不知道是不是真的不可能 我过去可以用 Perl Tk 做到
  • 传递 ManagedObjectContext - 这有效吗?

    我有一个正在开发的应用程序 有一个导航控制器 我的 appDelegate 将其 ManagedObjectContext 传递给导航控制器根控制器 如下所示 RootViewController rootViewController Ro
  • Pandas 日期时间格式不一致

    大约两周前我开始使用 pandas 库 学习新功能 对于以下问题 我将不胜感激 我有一列包含混合格式的日期 这是目前的 2 种格式 mm dd yyyy dd mm yyyy 数据集摘录 Dates 6 5 2016 7 5 2016 7
  • 动态壁纸教程

    我正在尝试从我发现的动态壁纸教程中执行以下操作here Do the actual drawing stuff private void doDraw Canvas canvas Bitmap b BitmapFactory decodeR
  • 如何在matlab中找到多维矩阵中每个“切片”的最大值?

    我有一个 n 维矩阵 Q 我可以通过以下方式找到最大值 最后一个维度 m max Q n 但是 我不知道如何找到最后一个的最大值 片 我需要一些 类似 的东西 m max Q n 1 n 例如 如果我有 A 1 1 2 3 4 50 6 A
  • MySQL如何即使连接数据为空也返回行

    我有三张桌子 product product description product store description product 该表有一行 product id description 1 regular description
  • Google Talk API 与 Android 应用集成

    我正在检查 google talk api 它使用 XMPP Here http code google com appengine docs java xmpp overview html Sending Chat Messages 我正
  • Facebook FB.Init 拒绝在框架中显示,因为它将“X-Frame-Options”设置为“DENY”

    按照 Facebook 的说明 Chrome 中的 FB Init 会生成 拒绝显示 https www facebook com connect ping 在一个 框架 因为它将 X Frame Options 设置为 DENY 但是 页
  • 为什么 JPasswordField 中的 getText() 被弃用?

    我以前没想过 只是我用了这个方法getPassword返回一个字符数组 我已经看到了getText方法是已弃用 但现在我想 为什么这个方法是已弃用 Java 文档解释道 已弃用 从 Java 2 平台 v1 2 开始 替换为getPassw
  • 为什么启动 Spyder 时在命令提示符下设置的环境变量不起作用

    我正在使用适用于 Python 的 Spyder Anaconda IDE 我正在 Spyder IDE 中编写代码 需要为 Theano 库设置几个环境变量 CPATH LIBRARY PATH 和 LD LIBRARY PATH 我正在
  • 带有来自 php 数组的多个标记的 Google 地图

    您好 有一系列城市 想要使用 javascript api v3 创建谷歌地图 当页面加载时 地图会不断跳转到每个标记 此外 即使我设置了高度和宽度 地图也会变得非常小 这是我生成地图的代码
  • 更改 WiFi-Direct IP 范围?在 Android WiFi-Direct 中强制使用 IPv6?

    我有两部 Android KitKat 手机 两者都作为组所有者运行 WiFi Direct 组 我们称它们为 GO1 和 GO2 我设法将 GO1 作为旧客户端连接到 GO2 而没有破坏任何 之前设置的 wifi direct 组 问题在
  • 如何使用 systemd 服务运行 pygame 脚本?

    我想使用 systemd 服务运行 pygame 脚本 按照以下步骤使用 systemd 服务运行 pygame 脚本 sudo systemctl daemon reload sudo systemctl enable service n
  • C++/CLI:将 LoadLibrary + GetProcAddress 与 exe 一起使用

    到目前为止 我有某种插件机制 在其中使用 LoadLibrary 和 GetProcAddress 加载 dll 来创建具体对象并返回公共接口 这工作得很好 直到我决定其中一个 dll 应该是一个 exe LoadLibrary 的文档说它
  • 显示按日期排序的数据

    public class Person public string Name get set public DateTime Created get set public class MyData public List
  • 在c中读取python的全局变量

    我正在尝试学习如何正确使用 Python C API 我实际上需要做的就是读取一个全局变量 在我的例子中是字典 但我从一个简单的整数变量开始 使用讨论 如何从 C 访问 Python 全局变量 以及答案的来源 http bytes com
  • 如何在所有活动中显示导航抽屉?

    我有一个Navigation Drawer这应该出现在我所有的活动中 我看到了很多与此类似的问题 并找到了一个解决方案 例如 Extending the MainActivity with the Other Activity 所以我将我的
  • 扩展基于 Acumatica 投影的 DAC 查询

    有没有办法扩展 修改投影 DAC 的投影查询 例如 如果我需要向投影添加连接语句 然后将新连接的表用于可用字段 向 PXCacheExtension 添加自定义字段可以按预期工作 但在 PXCacheExtension DAC 顶部指定 P
  • SQL查询查找当月的最后一天?

    SELECT DATEADD s 1 DATEADD mm DATEDIFF m 0 GETDATE 1 0 LastDay CurrentMonth 大家好 我有一个查询来查找当月的最后一天 这肯定运行良好 但我无法理解它 因为我有其他类
  • 为什么原子语句需要锁提示?

    Question 对以下语句应用锁有什么好处 同样 如果我们不包含这些提示 我们会 看到什么问题 即它们是否可以防止竞争条件 提高性能或其他什么 询问它们可能是为了防止出现一些我没有考虑过的问题 而不是我假设的竞争条件 注意 这是此处提出的