文章目录
前言
锁是一种同步机制,用于在多线程或多进程环境中对共享资源进行互斥访问,从而避免竞争条件和数据不一致问题。
分布式锁是一种在分布式系统中实现互斥访问的锁,允许多个进程或节点使用同一个锁来协调它们的操作。分布式锁通常使用分布式协议来确保锁的一致性和可靠性,例如 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) {
/* 写入数据 */ }
}