分布式锁
约 589 字大约 2 分钟
2025-02-12
什么是分布式锁
MirageFramework的分布式锁是一种机制,用于在分布式系统中实现互斥访问共享资源的功能。它基于原子操作特性,提供了一种轻量级的锁机制。
在分布式系统中,由于多个节点之间的并发操作,可能会导致数据的竞争和资源冲突的问题。分布式锁能够解决这个问题,确保在同一时刻只有一个节点能够获取到锁,从而保证了共享资源的正确访问。
IScopedKeyLocker
通过依赖注入 IScopedKeyLocker 对象,即可使用 MirageFramework 自带的分布式锁功能。
提示 IScopedKeyLocker 是通过 ZAddScoped 方式注册的,所以它会自动在请求结束后(如果有分布式事务,则是事务结束后)自动释放。在释放时,它会自动释放所有被锁住的Key。
如果微服务器宕机,分布式锁会在2秒内释放。
IKeyLocker
IKeyLocker是以单例模式注册的,意味着它只会在程序运行期间创建一次并且不会被释放。因此,在调用它来锁定资源时,请务必编写代码确保在使用完后释放Key。
案例
背景
假设当前数据库中存在一个名为UserInfo的表,其中的Name字段不允许包含重复的值。由于我正在使用Postgresql 12.9,如果我为Name字段创建一个唯一值索引,那么在开启事务并插入数据时,会对整个表进行锁定,导致其他事务无法向该表插入数据,从而造成阻塞的情况发生。
为了提高性能,我决定放弃使用唯一索引,并转而使用分布式锁来避免Name字段出现重复值的情况。这样可以避免表级锁对其他事务的影响,提升系统的并发性能。
具体实现代码如下:
public class DemoController : BaseController
{
private readonly IScopedKeyLocker _scopedKeyLocker;
public DemoController(IScopedKeyLocker scopedKeyLocker)
{
_scopedKeyLocker = scopedKeyLocker;
}
public async Task CreateUser(string userName)
{
this.CurrentDBContext.BeginTransaction();
//利用分布式锁,防止数据库产生同样的用户名
if (!_scopedKeyLocker.TryLock($"CreateUser_{userName}"))
throw new ServiceException("其他线程正在添加同样的用户");
if(await this.CurrentDBContext.UserInfo.AnyAsync(m=>m.Name == userName))
{
throw new ServiceException("用户名已存在");
}
await this.CurrentDBContext.InsertAsync(new UserInfo {
Name = userName
});
}
}