2012-08-13 68 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. 
} 
相關問題