脚本与SQL Server中的表并发运行

时间:2014-03-16 20:55:40

标签: python sql sql-server concurrency isolation-level

我有相同的Python脚本我需要在多个服务器上运行,所有服务器都针对数据库服务器上的同一个表。该脚本需要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

该脚本执行以下操作:

  1. 抓取所有类型为X的记录(在一次交易中)DirtyBitUnUsedOwnerNULL

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

  3. 在Python中对数据执行一些操作。

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

  5. 由于脚本在多个服务器上运行,因此DirtyBit / Owner组合可以确保脚本不会相互踩踏。另请注意,表中的每一行都独立于所有其他行。

    问题:这是让脚本同时运行的明智方法吗?无论如何,数据库可以为我处理这个问题(可能更改Transaction Isolation Level?)。理想情况下,如果脚本碰巧同时运行,我想要这个:

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

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

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

2 个答案:

答案 0 :(得分:2)

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

在您的情况下,您要做的是,事实上,将访问序列化到您的表中,而不仅仅是更新。也就是说,只允许一个线程(事务)获取所需的数据(DirtyBitUnUsedOwnerNULL)并将这些行标记为“已使用” 。我很确定您当前的解决方案无法正常工作。为什么?考虑这样的情况:

  1. 交易1开始
  2. 交易2开始
  3. 事务1从表中读取数据
  4. 事务2从表中读取数据 - 允许它以共享锁定模式。它读取与事务1相同的数据
  5. 事务1更新表
  6. 事务2想要更新表,但它被事务1阻止 - 它保持
  7. 事务1提交
  8. 现在,事务2可以更新数据并提交它们
  9. 因此,事务1和2都读取相同的行,并且两个服务器上的脚本都将对它们进行操作。您可以轻松地在数据库上手动重现这样的场景。

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

    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)将导致其他事务等待此事务提交或回滚 - 它们将无法读取数据。这会解决您的问题吗?

    但是......如果你能在这种特殊情况下避免并发,我建议你这样做(因为我的回复的第一段)。

答案 1 :(得分:1)

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

scheduled job来说,这似乎是一个很好的问题,通过sp_getapplock控制并发: