Python SQL Server中针对表并发运行的脚本

Python SQL Server中针对表并发运行的脚本,python,sql,sql-server,concurrency,isolation-level,Python,Sql,Sql Server,Concurrency,Isolation Level,我需要在多个服务器上运行相同的Python脚本,所有这些脚本都针对DB服务器上的同一个表。脚本运行需要5-20秒,并且必须每5分钟运行一次 Server1 ---> ------------- | DB Table | Server2 ---> ------------- 脚本将查看一个如下所示的表: Type | many other fields | DirtyBit | Owner ---------------------------

我需要在多个服务器上运行相同的Python脚本,所有这些脚本都针对DB服务器上的同一个表。脚本运行需要5-20秒,并且必须每5分钟运行一次

Server1 --->  -------------
              |  DB Table |
Server2 --->  -------------
脚本将查看一个如下所示的表:

Type | many other fields | DirtyBit  |  Owner
 --------------------------------------------
  X  | ...               | UnUsed    |   NULL
  X  | ...               | UnUsed    |   NULL
  X  | ...               | UnUsed    |   NULL
  Y  | ...               | UnUsed    |   NULL
  Y  | ...               | UnUsed    |   NULL
begin transaction;

select * from test where DirtyBit = 'UnUsed' and Owner is null (TABLOCKX);

update test set DirtyBit = 'Used', Owner = 'Server1' where id in (...);

commit;
该脚本执行以下操作:

  • 抓取
    DirtyBit
    UnUsed
    Owner
    NULL
    的X类型(在事务中)的所有记录

  • 更新所有记录,将
    DirtyBit
    设置为
    InUse
    ,将
    Owner
    设置为
    Server1

  • 在Python中对数据执行一些操作

  • 根据3中的操作更新所有记录。将
    DirtyBit
    设置回
    UnUsed
    ,并将
    Owner
    设置回
    NULL

  • 由于脚本在多台服务器上运行,DirtyBit/Owner组合可以确保脚本不会相互重叠。另外,请注意,表中的每一行都独立于所有其他行

    问题:这是让脚本同时运行的明智方法吗?数据库是否可以为我处理此问题(可能是更改数据库?)。理想情况下,如果脚本恰好同时运行,我希望:

  • 服务器1上的脚本开始运行

  • 服务器2上的脚本开始运行,注意到1正在运行,因此决定不需要运行

  • 服务器1上的脚本完成,更新所有数据


  • 我不会采取你在这里使用的方法。像这样的本土解决方案往往很脆弱


    对于a来说,这似乎是一个好问题,通过以下方式控制并发性:

    开发基于并发访问和修改数据的解决方案始终是一件非常明智的事情。他们也容易犯错误,这些错误很少发生,而且很难找到

    在您的情况下,实际上,您要做的是序列化对表的访问,而不仅仅是更新。也就是说,只允许一个线程(事务)获取它需要的数据(其中,
    DirtyBit
    UnUsed
    ,而
    Owner
    NULL
    ),并将这些行标记为“used”。我很确定您当前的解决方案不能正常工作。为什么?考虑这样一个场景:

  • 交易1开始
  • 交易2开始
  • 事务1从表中读取数据
  • 事务2从表中读取数据-允许在共享锁模式下执行。它读取与事务1相同的数据
  • 事务1更新表
  • 事务2想要更新表,但它被事务1阻止-它保持不变
  • 事务1提交
  • 现在事务2可以更新数据并提交它们
  • 因此,事务1和事务2都读取相同的行,并且两台服务器上的脚本都将在它们上运行。您可以轻松地在数据库上手动复制这样的场景

    可以避免显式获取独占表锁。这看起来像这样:

    Type | many other fields | DirtyBit  |  Owner
     --------------------------------------------
      X  | ...               | UnUsed    |   NULL
      X  | ...               | UnUsed    |   NULL
      X  | ...               | UnUsed    |   NULL
      Y  | ...               | UnUsed    |   NULL
      Y  | ...               | UnUsed    |   NULL
    
    begin transaction;
    
    select * from test where DirtyBit = 'UnUsed' and Owner is null (TABLOCKX);
    
    update test set DirtyBit = 'Used', Owner = 'Server1' where id in (...);
    
    commit;
    
    在这里,
    (TABLOCKX)
    将导致其他事务等待该事务提交或回滚-它们将无法读取数据。这能解决你的问题吗


    但是。。。如果您可以在这个特定的情况下避免并发,我建议您这样做(因为我的回答的第一段)

    你能评论/推测一下你认为这个特殊的解决方案在哪里是脆弱的吗?我认为计划作业不适合这种情况,因为脚本在数据库之外做了很多工作。我一定会尝试一下
    sp_getapplock
    ,如果这个答案可行的话,我会接受它。谢谢。您在事务中运行四个步骤中的哪一个?它们都在一个事务中,每一步都在单独的事务中,还是以另一种方式?您使用什么事务隔离级别?因此,当我在开始选择所有行时,我可以简单地使用
    TABLOCKX
    ,在这种情况下,没有理由使用
    Owner
    DirtyBit
    来控制并发性,我可以删除这些列。正确吗?
    TABLOCKX
    设置表锁,直到事务结束。这意味着,如果要在事务中执行耗时的操作,则整个表将对其他事务保持锁定-它们将等待并可能耗尽事务时间。我认为最好是在一个事务中获取行并更新表(描述中的前两个步骤),然后在第一个事务之外对脚本中的数据进行操作(这样就不会阻止对表的访问),然后在完成任务后更新表(步骤4)。当然,如果表可以长时间锁定,那么您可以放弃使用这些列。这取决于此表的作用。