Sql server 使用SQL Server创建跨计算机互斥体
我有几台计算机使用相同的数据库(SQL Server 2008) 我正在尝试使用数据库在所有这些计算机之间同步任务 每个任务都由一个guid表示,该guid是锁id(如果与互斥体比较,则为互斥体名称) 我有一些想法,但我认为它们是一种黑客,我希望这里的人会有更好的解决方案:Sql server 使用SQL Server创建跨计算机互斥体,sql-server,sql-server-2008,synchronization,locking,Sql Server,Sql Server 2008,Synchronization,Locking,我有几台计算机使用相同的数据库(SQL Server 2008) 我正在尝试使用数据库在所有这些计算机之间同步任务 每个任务都由一个guid表示,该guid是锁id(如果与互斥体比较,则为互斥体名称) 我有一些想法,但我认为它们是一种黑客,我希望这里的人会有更好的解决方案: 创建一个新表“锁定”每一行由一个guid组成,在事务中以独占方式锁定表行,并在事务完成时完成/恢复事务 在锁名称为锁id guid的事务中使用sp_getapplock 我认为保持事务运行不是很好。。。我想也许有一种解决方案
sp_getapplock
我认为保持事务运行不是很好。。。我想也许有一种解决方案不需要我持有打开的事务或会话?我会推荐一些完全不同的解决方案:。不要显式锁定任务,而是将任务添加到处理队列中,让队列处理程序将任务出列并执行工作。额外的解耦还将有助于可伸缩性和吞吐量。如果您仅有的共享资源是数据库,那么在解决方案中使用事务锁可能是您的最佳选择。如果我理解另一个答案中由@Remus Rusanu链接的文章,它还要求在事务中退出队列 这在某种程度上取决于您计划打开这些锁的时间。如果你是
在CreateTransaction实用程序方法中,您可以在创建事务后立即调用sp_getapplock。然后整个过程(包括互斥)提交或回滚到一起。我已经创建了一个小类将测试和反馈 用途是:
GlobalMutex globalMutex = new GlobalMutex(
new SqlConnection(""),
"myGlobalUniqueLockName",
new TimeSpan(0, 1, 0)
);
using (globalMutex.Lock)
{
// do work.
}
我知道把人们推荐给谷歌不是一个好的做法,但我认为这是一个有效的例外。如果OP选择这样做,他可以在谷歌上搜索“分布式同步”,并找到很多关于该主题的好资源。感谢您的回复,但是这个主题通常非常复杂,并且与服务器之间的数据同步(复制、合并等)更相关。我只想在这些计算机之间持有一个独占锁。我唯一拥有的共享资源是一个数据库,我需要使用这个数据库获得一个独占锁。我无法在这些计算机之间创建共享/同步队列。抱歉。但不幸的是,这对我仍然没有帮助。这是一个用户发起的任务-它发生在其中一台计算机上,当它运行时,我不希望用户能够在任何其他计算机上发起任务。我不明白排队对我有什么帮助,我明白了。使用会话绑定的applock(而不是事务绑定的applock)应该可以满足您的需要。如果sp返回<0,我会抛出一个异常。请参阅-btw处的返回代码值。ExecuteOnQuery的结果不是sp的结果
public class GlobalMutex
{
private SqlCommand _sqlCommand;
private SqlConnection _sqlConnection;
string sqlCommandText = @"
declare @result int
exec @result =sp_getapplock @Resource=@ResourceName, @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.
}