2012-08-13 73 views
2

跨计算机互斥我必须使用相同的数据库(SQL Server 2008中)创建使用SQL Server

我试图使用数据库的所有这些计算机之间同步任务,几台电脑。

每个任务由一个GUID,它是锁定-ID(如果比较互斥,这将是互斥名)

我有几个想法代表,但我认为他们是善良的黑客,以及希望有人在这里将有一个更好的解决方案:

  1. 创建新表“锁”的每一行由一个GUID的,只在一个交易锁定表行 - 和完整/完成后恢复交易。
  2. 使用sp_getapplock在一个事务中,其中锁定名称是锁ID GUID

我觉得拿着事务的运行也不是那么好...我想也许有,不需要我一个解决方案举行公开交易或会议?

+0

我知道,这不是很好的做法,是指人们对谷歌,但我认为这是一个有效的例外。如果OP选择这样做,他可以谷歌“分布式同步”,并找到关于该主题的很多好资源。 – 2012-08-13 14:28:48

+0

感谢您的回应,但是这个主题通常非常复杂,并且与服务器之间的数据同步(复制,合并等)有关。我只想在这些电脑之间保持独家锁定。 – 2012-08-13 14:31:50

回答

0

我会推荐一些相当不同的东西:use a queue。不是明确地锁定任务,而是将任务添加到处理队列中,并让队列处理程序使任务出队并执行工作。额外的解耦还将有助于可扩展性和吞吐量。

+0

我拥有的唯一共享资源是数据库,我需要使用此数据库获得排他锁。我无法在这些计算机之间创建共享/同步队列。 – 2012-08-13 15:18:38

+1

阅读文章链接。 – 2012-08-13 15:41:34

+0

对不起。但不幸的是,这仍然不能帮助我。这是用户发起的任务 - 它发生在其中一台计算机上,并且在运行时,我不希望用户能够在任何其他计算机上启动任务。我不明白队列会如何帮助我。 – 2012-08-13 15:44:46

0

如果您拥有的唯一共享资源是数据库,那么使用事务锁作为解决方案的一部分可能是您的最佳选择。如果我了解@Remus Rusanu在其他答案中链接的发言权,那么它也需要出列交易。

这取决于你计划持续打开这些锁的时间。如果您是...

  1. 问题上的锁ID强制系列化一个简短的操作
  2. 已经打开交易反正来完成该操作

...那么你的选项2可能是最简单也是最可靠的。我已经在生产系统中使用了几年没有问题的解决方案。如果您将互斥体的创建与事务的创建捆绑在一起并将其全部包装在“使用”块中,则变得更加容易。

using (var transaction = MyTransactionUtil.CreateTransaction(mutexName)) 
{ 
    // do stuff 
    transaction.Commit(); 
} 

在您的CreateTransaction实用程序方法中,创建事务后立即调用sp_getapplock。然后整个事情(包括互斥体)都会一起提交或回滚。

1

我已经把一个小类将测试和反馈

public class GlobalMutex 
{ 
    private SqlCommand _sqlCommand; 
    private SqlConnection _sqlConnection; 

    string sqlCommandText = @" 

     declare @result int 
     exec @result =sp_getapplock @[email protected], @LockMode='Exclusive', @LockOwner='Transaction', @LockTimeout = @LockTimeout 

    "; 

    public GlobalMutex(SqlConnection sqlConnection, string unqiueName, TimeSpan lockTimeout) 
    { 
     _sqlConnection = sqlConnection; 
     _sqlCommand = new SqlCommand(sqlCommandText, sqlConnection); 
     _sqlCommand.Parameters.AddWithValue("@ResourceName", unqiueName); 
     _sqlCommand.Parameters.AddWithValue("@LockTimeout", lockTimeout.TotalMilliseconds); 
    } 

    private readonly object _lockObject = new object(); 
    private Locker _lock = null; 
    public Locker Lock 
    { 
     get 
     { 
      lock(_lockObject) 
      { 
       if (_lock != null) 
       { 
        throw new InvalidOperationException("Unable to call Lock twice"); // dont know why 
       } 
       _lock = new Locker(_sqlConnection, _sqlCommand); 
      } 
      return _lock; 
     } 
    } 

    public class Locker : IDisposable 
    { 
     private SqlTransaction _sqlTransaction; 
     private SqlCommand _sqlCommand; 

     internal Locker(SqlConnection sqlConnection, SqlCommand sqlCommand) 
     { 
      _sqlCommand = sqlCommand; 
      _sqlTransaction = sqlConnection.BeginTransaction(); 
      _sqlCommand.Transaction = _sqlTransaction; 
      int result = sqlCommand.ExecuteNonQuery(); 
     } 

     public void Dispose() 
     { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
     } 

     protected virtual void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       _sqlTransaction.Commit(); // maybe _sqlTransaction.Rollback() might be slower 
      } 
     } 
    } 
} 

用法为:

GlobalMutex globalMutex = new GlobalMutex(
    new SqlConnection(""), 
    "myGlobalUniqueLockName", 
    new TimeSpan(0, 1, 0) 
); 


using (globalMutex.Lock) 
{ 
    // do work. 
} 
相关问题