在我的 SQL Server 上使用事务会阻止另一台主机读取表中的行吗?

问题描述 投票:0回答:1

假设我有一个不断更新要处理的任务的表。该表中的一列是

Status
,当任务首次输入表中时,是
Status = 'submitted'
。当主机正在处理任务时,它将
Status
更改为
'processing'
,当任务的数据处理完成时,它将
Status
更新为
'completed'

我将有多个主机将访问此特定表以检查是否已添加任务。

这是我目前拥有的代码:

conn = get_mssql_conn().connect()

result = conn.execute(
                    "SELECT TOP 1 * "
                    "FROM ad_tool_enhancement "
                    "WHERE status = 'submitted' "
                    "ORDER BY jobid ASC" 
                    )

row = result.fetchall()
   
if row:
    jobid, Job_name, Email, status, date_submit, time_submit, process_time, json_data, Input, analysis_type, source, groupby = row[0]

    start = datetime.datetime.now()

    conn.execute(f"UPDATE ad_tool_enhancement "
                f"SET status = 'processing' "
                f"WHERE jobid = {jobid}"
                )
    
    #Then the task is processed                

当只有一台主机运行/处理任务时,这很好用,但将来会有更多,我相信我需要使用锁/事务来确保两台主机不会在同一时刻开始处理相同的任务。同时。

我感到困惑的是,我从未见过用于读取语句的事务(该部分与锁定的更新部分一样重要),所以我不确定它是否会起作用。

我尝试查看 SQL Alchemy 和 MS SQL Server 的文档,但没有找到针对我的问题的任何内容。

sql-server locking
1个回答
0
投票

SQL Server 中的每个事务都应用锁,甚至是 SELECT 语句。

SQL Shack 概述说明 https://www.sqlshack.com/locking-sql-server/

理论

为了满足答案的空间,此处进行了缩写。关于锁的文章已经有很多,我在这里写的内容是我几十年来所知道的。

理论上,只要事务较短或受影响的行范围有限,并发访问就可以。当许多行(主要取决于服务器资源)受到影响或由于延迟诱导而到 tempdb 的假脱机到位时,就会出现问题。

SELECT 需要共享锁。这对于其他线程来说意味着:读取可以,不允许更新。这样,SELECT 语句可确保从表/视图/过程读取的数据与与接收者的交互之间的数据不会更改。一旦收件人确认收到,共享锁就会被释放。

如果此时 UPDATE 尝试修改共享锁下的这些行之一的数据,则该行将处于休眠状态,直到 UPDATE 可以获得更新锁。获取它,更新行并释放锁。

如果锁持有时间过长,则可能会发生一个或多个进程陷入死锁的情况,即 SQL Server 引擎变得“无法取得任何进展而感到恼火”。然后尝试通过终止进程来“解决”问题。

升级

跟踪锁是 SQL Server 引擎的一部分,为了提高效率,它可以将锁从行升级到页、表到数据库。更少的跟踪=更快的工作。至少这个想法是这样的。

因此加载 5 行的 SELECT 可能具有行锁或页锁。如果表只有 5 行,那么表(共享)锁比 5 行锁更有效。

如果此时 SELECT 有表共享锁,而另一个进程带有 INSERT 那么这个操作就无法进行。由于 INSERT 无法为其操作获取足够的锁。理论上这不是问题,因为几毫秒后,表共享锁被 SELECT 释放,并且 INSERT 可以继续。

这种升级确实取决于,这个示例只是为了解释一个概念而缩写。或者一个进程按行发出数千条更新语句。通常,行更新锁在某些非常特定的条件下可以提升为页锁或表锁,因为 UPDATE 命令不必等待获取锁。

如何避免死锁 最糟糕的方法是发出带有提示“WITH (NOLOCK)”的 SELECT。预计用户会抱怨看到的数据不再存在。用户做出错误的决定是因为显示的数据已经在幕后移动。这是 SELECT 发出共享锁的主要原因 - 为了一致地显示数据。

缓解死锁

如果更新发生频率很高,那么也许一个想法是将一个表分成两部分 - 一部分受高频更新影响,另一部分则很可能只是被读取。

或者发布包含提示“WITH (ROWLOCK)”的更新可能会受到足够的限制,以使更新足够快且不具有侵入性。或者选择相同。

© www.soinside.com 2019 - 2024. All rights reserved.