【问题标题】:SQL Server Lock the Row the Read and Update not working in PythonSQL Server 锁定行读取和更新在 Python 中不起作用
【发布时间】:2021-08-09 03:21:09
【问题描述】:

对于可以同时运行多个进程的程序,我需要在 SQL Server 和 Python 中锁定一行,读取然后更新该行的一个字段,以避免竞争条件。

我正在使用以下代码,但在 Python 中,它会更新一个 Id(即 3411)并返回另一个 Id(即 3071)。如果我直接在 SSMS 中运行相同的 SQL 代码,它会更新并返回相同的 Id。

import pyodbc

def read_and_update_files_queue(self):
    conn = pyodbc.connect('Driver={SQL Server};'
                    'Server='+self.server+';'
                    'Database='+self.database+';'
                    'Trusted_Connection=yes;')
    cursor = conn.cursor()
    sql_query=f'''
                BEGIN TRANSACTION;
                    DECLARE @Temp_ID AS INT
                    SELECT TOP 1 @Temp_ID=Id 
                    FROM dbo.[Queue] 
                    WITH(XLOCK, ROWLOCK)
                    WHERE [BQ_status] IS NULL and upload_date IS NOT NULL

                    Select TOP 1 
                    Id,GCS_path,file_first_upload 
                    FROM dbo.[Queue]
                    WITH(XLOCK, ROWLOCK) 
                    WHERE Id=@Temp_ID


                    UPDATE dbo.[Queue] SET BQ_status='Loading' 
                    WHERE Id=@Temp_ID

                COMMIT TRANSACTION;
                '''
    cursor.execute(sql_query)
    cursor.commit()

怎么了? 代码基于此: In SQL Server, how can I lock a single row in a way similar to Oracle's "SELECT FOR UPDATE WAIT"? 谢谢!!

【问题讨论】:

  • 帮自己一个大忙,并在 SQLSERVER 上的存储过程中使用 TRY CATCH WITH A ROLLBACK 处理此事务。另外你用第二个选择语句完成了什么?
  • 在秒语句中,我不仅需要返回 ID,还需要返回名为 GCS_path、file_first_upload 的字段,这些字段稍后将在 python 程序中使用。为什么需要try catch? (如果我在不存在 ID 时运行此事务,它会按预期工作(它什么也不做))
  • 然后使用您的 First select 语句执行此操作。只需为 GCS_path、file_first_upload 声明变量并捕获其中的值以便稍后在 python 程序中重用。消除第二个 select 语句。

标签: python sql-server concurrency locking


【解决方案1】:

因为前两条语句没有修改数据,you need the UPDLOCK hint,否则它不会持有锁直到事务结束,不管any other hints, including XLOCK

但是您可以在一个语句中完成所有操作,这里不需要UPDLOCK,也不需要显式事务(该语句无论如何都在事务中运行)

UPDATE TOP (1) q
    SET BQ_status = 'Loading'
    OUTPUT inserted.Id, inserted.GCS_path, inserted.file_first_upload
FROM dbo.[Queue] AS q WITH (SERIALIZABLE, ROWLOCK)
    WHERE q.[BQ_status] IS NULL and q.upload_date IS NOT NULL;

虽然SERIALIZABLE 有时只使用共享的S 锁,但适用于SELECT 或DML 语句中未修改的连接表。 p>

在这种情况下,由于Queue 是正在修改的表,所以在找到正确的行时会使用U 锁,这会禁止U 锁来自其他事务,这将被阻塞等待(一个事务正在执行SELECT 不会被阻止)。然后,当一行即将被修改时,U锁升级为X,并被修改。

所以这里不用担心竞争条件,也不需要UPDLOCK 提示。

【讨论】:

  • 谢谢。这样可行。但据我所知,Serializable 创建了一个共享锁,所以其他进程仍然可以读取该行,还是我错了?我认为为了避免竞争条件,锁应该是独占锁而不是共享锁。
  • 不,这仅适用于未修改的表,否则会采用 U 锁定,这将阻止其他修饰符。除非正在修改表,否则您不能强制使用X 锁定。已经澄清了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多