記事ディレクトリ
序文
ロックは、マルチスレッドまたはマルチプロセス環境で共有リソースへの相互排他的アクセスを提供し、競合状態やデータの不整合を回避するために使用される同期メカニズムです。
分散ロックは、分散システムで相互排他的アクセスを実装するロックであり、複数のプロセスまたはノードが同じロックを使用して操作を調整できるようにします。分散ロックは通常、ZooKeeper、Redis などの分散プロトコルを使用してロックの一貫性と信頼性を確保します。分散ロックのアプリケーション シナリオには、高い同時実行性でのデータ更新、分散タスク スケジューリング、分散トランザクションなどが含まれます。
1. DistributedLockの分散ロック
1.コンセプト
DistributedLock は、分散システムで同期またはコラボレーションを実現するメカニズムです。分散システムでは、複数のプロセス、スレッド、またはサービスが共有リソースにアクセスしたり、同じタスクを同時に実行したりすることがあります。共有リソースへのアクセスが制御されていないと、データの不整合や予測できない実行結果が発生する可能性があります。分散ロックは、異なるノード間の操作を調整することにより、共有リソースのセキュリティと正確性を保証します。
分散ロックを実装するには多くの方法がありますが、最も一般的なのは、相互排他アルゴリズム (Zookeeper ベースの実装など)、キャッシュ ベース (Redis 実装など) などに基づくものです。これらのメカニズムを通じて、分散ロックはロック操作とロック解除操作を実装し、同時に 1 つのプロセス、スレッド、またはサービスのみが共有リソースにアクセスできるようにすることができます。
DistributedLock ソースコード URL: https://github.com/madelson/DistributedLock
2.基本的な使い方
DistributedLock は、分散ロック用のオープン ソース ライブラリであり、さまざまな分散ロック実装を提供し、さまざまなデータ ストレージ テクノロジを使用して分散ロックをサポートできます。複数の分散ロック インスタンスの共存をサポートし、さまざまなシナリオに適用できます。
使用:
2.1 分散ロック.SqlServer
SqlServer をデータ ストレージ テクノロジとして使用するには、まず SqlServer データベースをインストールし、「Locks」という名前のデータベースを作成する必要があります。次に、次のコードを使用して分散ロックを操作します。
var connectionString = "your connection string";
var lockName = "your lock name";
using(var sqlLock = new SqlDistributedLock(lockName, connectionString))
{
try
{
sqlLock.TryAcquire(TimeSpan.FromSeconds(30));
// do something that needs synchronization
}
finally
{
sqlLock.Release();
}
}
2.2 分散ロック.Postgres
Postgres をデータ ストレージ テクノロジとして使用するには、まず Postgres データベースをインストールし、「distributed_locks」という名前のデータベースを作成する必要があります。次に、次のコードを使用して分散ロックを操作します。
var connectionString = "your connection string";
var lockName = "your lock name";
using(var postgresLock = new PostgresDistributedLock(lockName, connectionString))
{
try
{
postgresLock.TryAcquire(TimeSpan.FromSeconds(30));
// do something that needs synchronization
}
finally
{
postgresLock.Release();
}
}
2.3 分散ロック.MySql
MySql をデータ ストレージ テクノロジとして使用するには、まず MySql データベースをインストールし、「Locks」という名前のデータベースを作成する必要があります。次に、次のコードを使用して分散ロックを操作します。
var connectionString = "your connection string";
var lockName = "your lock name";
using(var mysqlLock = new MySqlDistributedLock(lockName, connectionString))
{
try
{
mysqlLock.TryAcquire(TimeSpan.FromSeconds(30));
// do something that needs synchronization
}
finally
{
mysqlLock.Release();
}
}
2.4 分散ロック.Oracle
データ ストレージ テクノロジとして Oracle を使用するには、まず Oracle データベースをインストールし、「Locks」という名前のデータベースを作成する必要があります。次に、次のコードを使用して分散ロックを操作します。
var connectionString = "your connection string";
var lockName = "your lock name";
using(var oracleLock = new OracleDistributedLock(lockName, connectionString))
{
try
{
oracleLock.TryAcquire(TimeSpan.FromSeconds(30));
// do something that needs synchronization
}
finally
{
oracleLock.Release();
}
}
2.5 分散ロック.Redis
Redis をデータ ストレージ テクノロジとして使用するには、最初に Redis データベースをインストールする必要があります。次に、次のコードを使用して分散ロックを操作します。
var connectionString = await ConnectionMultiplexer.ConnectAsync(connectionString);
// StackExchange.Redis
var lockName = "your lock name";
using(var redisLock = new RedisDistributedLock(lockName, connectionString))
{
try
{
redisLock.TryAcquire(TimeSpan.FromSeconds(30));
// do something that needs synchronization
}
finally
{
redisLock.Release();
}
}
2.6 分散ロック.Azure
Azure をデータ ストレージ テクノロジとして使用するには、最初に Azure Storage を作成する必要があります。次に、次のコードを使用して分散ロックを操作します。
var connectionString = "your connection string";
var lockName = "your lock name";
using(var azureLock = new AzureDistributedLock(lockName, connectionString))
{
try
{
azureLock.TryAcquire(TimeSpan.FromSeconds(30));
// do something that needs synchronization
}
finally
{
azureLock.Release();
}
}
2.7 分散ロック.ZooKeeper
ZooKeeper をデータ ストレージ テクノロジとして使用するには、まず ZooKeeper をインストールする必要があります。次に、次のコードを使用して分散ロックを操作します。
var connectionString = "your connection string";
var lockName = "your lock name";
using(var zooKeeperLock = new ZooKeeperDistributedLock(lockName, connectionString))
{
try
{
zooKeeperLock.TryAcquire(TimeSpan.FromSeconds(30));
// do something that needs synchronization
}
finally
{
zooKeeperLock.Release();
}
}
2.8 分散ロックファイルシステム
データ ストレージ テクノロジとしてファイル システムを使用する場合、ロック ストレージ ディレクトリとしてフォルダーを指定する必要があります。次に、次のコードを使用して分散ロックを操作します。
var directoryPath = "your directory path";
var lockName = "your lock name";
using(var fileLock = new FileSystemDistributedLock(lockName, directoryPath))
{
try
{
fileLock.TryAcquire(TimeSpan.FromSeconds(30));
// do something that needs synchronization
}
finally
{
fileLock.Release();
}
}
2.9 分散ロック.WaitHandles
データ ストレージ テクノロジとして WaitHandles を使用するには、「Global\」 + lockName という名前の WaitHandle を指定する必要があります。次に、次のコードを使用して分散ロックを操作します。
var lockName = "your lock name";
using(var waitHandleLock = new WaitHandleDistributedLock(lockName))
{
try
{
waitHandleLock.TryAcquire(TimeSpan.FromSeconds(30));
// do something that needs synchronization
}
finally
{
waitHandleLock.Release();
}
}
3. 読み書きロック
読み取り/書き込みロックは、読み取りが多く書き込みが少ない場合に同時アクセスの効率を向上させることができるマルチスレッド同時実行制御メカニズムです。読み取り操作と書き込み操作は相互に排他的です。つまり、1 つのスレッドが書き込みを行っている間、他のスレッドは読み取りも書き込みもできませんが、複数のスレッドは同時にデータを読み取ることができます。読み取り/書き込みロックは読み取りロックと書き込みロックに分かれており、読み取りロックと書き込みロックは共有リソースへのアクセス権を相互に占有することができます。スレッドが書き込みロックを占有している場合、書き込みロックが解放されるまで、他のスレッドは読み取りでも書き込みでもそのスレッドにアクセスできません。1 つのスレッドが読み取りロックを占有している場合、他のスレッドは引き続き読み取りロックを占有できますが、すべての読み取りロックが解放されるまで書き込みロックはブロックされます。読み取り/書き込みロックによって効率が向上する理由は、複数のスレッドが同時にデータを読み取る必要がある場合、読み取りロックは同時に複数のスレッドによって占有できるのに対し、書き込みロックは 1 つのスレッドによってのみ占有できるためです。これにより、読み取りと書き込みの競合が回避され、パフォーマンスのボトルネックになります。
class DistributedCache
{
// 使用 Sql Server 实现
private readonly SqlDistributedReaderWriterLock _cacheLock =
new SqlDistributedReaderWriterLock("DistributedCache", connectionString);
// 如果缓存中存在 key, 则返回,否则,生成新的 key,写入后并返回
public async Task<object> GetOrCreateAsync(string key, Func<string, object> valueFactory)
{
// 首先,用 Read 锁尝试获取缓存数据
await using (await this._cacheLock.AcquireReadLockAsync())
{
var cached = await this.GetValueOrDefaultNoLockAsync(key);
if (cached != null) {
return cached; }
}
// 缓存不存在,用 Write 锁, 写入数据并返回
await using (await this._cacheLock.AcquireWriteLockAsync())
{
// double-check: 检查是否已经被其他的进程写入数据
var cached = await this.GetValueOrDefaultNoLockAsync(key);
if (cached != null) {
return cached; }
// 写入数据并返回
var generated = valueFactory(key);
await this.SetValueAsync(key, generated);
return generated;
}
}
private async Task<object?> GetValueOrDefaultNoLockAsync(string key) {
/* 读取数据 */ }
private async Task SetValueAsync(string key, object value) {
/* 写入数据 */ }
}