【愚公系列】2023年09月 .NET CORE工具案例-DistributedLock的分布式锁


前言

锁是一种同步机制,用于在多线程或多进程环境中对共享资源进行互斥访问,从而避免竞争条件和数据不一致问题。

分布式锁是一种在分布式系统中实现互斥访问的锁,允许多个进程或节点使用同一个锁来协调它们的操作。分布式锁通常使用分布式协议来确保锁的一致性和可靠性,例如 ZooKeeper、Redis 等。分布式锁的应用场景包括高并发下的数据更新、分布式任务调度、分布式事务等。

一、DistributedLock的分布式锁

1.概念

DistributedLock(分布式锁)是一种在分布式系统中实现同步或协作的机制。在分布式系统中,多个进程、线程或服务可能同时访问共享资源或执行相同的任务。如果不加控制地访问共享资源,会导致数据不一致或执行结果不可预测的问题。分布式锁通过协调不同节点之间的操作,保证共享资源的安全和正确性。

分布式锁的实现方式有多种,其中比较常见的是基于互斥算法(如基于Zookeeper的实现)、基于缓存(如Redis的实现)等。通过这些机制,分布式锁可以实现锁定和解锁的操作,并确保同一时刻只有一个进程、线程或服务可以访问共享资源。

DistributedLock源码网址:https://github.com/madelson/DistributedLock

在这里插入图片描述

2.基本使用

DistributedLock是一个分布式锁的开源库,它提供了多种分布式锁实现方式,可以使用不同的数据存储技术来支持分布式锁。它支持多个分布式锁实例共存,可以适用于多种场景。

使用:

2.1 DistributedLock.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 DistributedLock.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 DistributedLock.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 DistributedLock.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 DistributedLock.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 DistributedLock.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 DistributedLock.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 DistributedLock.FileSystem

使用文件系统作为数据存储技术,需要指定文件夹作为锁的存储目录。然后使用以下代码进行分布式锁的操作:

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 DistributedLock.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.读写锁

读写锁是一种多线程并发控制机制,在读多写少的情况下可以提高并发访问效率。读操作和写操作之间是互斥的,即一个线程在写时其他线程不能读写,但是多个线程可以同时读取数据。读写锁分为读锁和写锁,读锁和写锁可以互斥地占用对共享资源的访问权。如果一个线程占用了写锁,则其他线程无论是读还是写都无法访问,直到写锁被释放。如果一个线程占用了读锁,则其他线程可以继续占用读锁,但写锁将被阻塞,直到所有的读锁都被释放。读写锁能够提高效率的原因就在于,当有多个线程同时需要读取数据时,读锁可以被多个线程同时占用,而写锁只能有一个线程占用,从而避免了读写冲突带来的性能瓶颈。

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) {
    
     /* 写入数据 */ }
}

猜你喜欢

转载自blog.csdn.net/aa2528877987/article/details/132877023