如何使用 Redis 实现分布式锁

在多线程的环境下,为了保证一个代码块在同一时间只能由一个线程访问。现在公司都是流行分布式架构,在分布
首页 新闻资讯 行业资讯 如何使用 Redis 实现分布式锁

在多线程的环境下,为了保证一个代码块在同一时间只能由一个线程访问。现在公司都是流行分布式架构,在分布式环境下,如何保证不同节点的线程同步执行呢?

实际上,对于分布式场景,我们可以使用分布式锁,它是控制分布式系统之间互斥访问共享资源的一种方式。对于每个锁,最好有一个唯一id,保证不会错误解锁。(例如 :A锁与B锁的key相同,在A锁过期的一瞬间,B锁进行解锁,若不校验锁id,会导致A锁被解锁);Redis提供了SETNX(set if not exists),仅在key不存在时插入value;Redis在2.6.12版本提供了SET函数的重载,支持仅在key不存在时插入带有过期时间的value;虽然Redis没有提供仅在value相同时删除的命令,但是在2.6.0版本提供了EXAL用于执行脚本,通过该脚本可以;仅在value相同时删除这一功能。

e6ff980985cba526fb0570848dd57107f1828b.png

下面介绍实现分布式锁。

引入依赖

using ServiceStack.Redis;

锁实现过程

privatestaticreadonly string ScriptSetIfAbsent="return redis.call('SET',KEYS[1],ARGV[1],'EX',ARGV[2],'NX')";privatestaticreadonly string ScriptDeleteIfEqualValue=@"ifredis.call('GET',KEYS[1])==ARGV[1]thenreturnredis.call('DEL',KEYS[1])elsereturn'FALSE'end";
/// <summary>/// 加锁/// </summary>/// <param name="key">锁key</param>/// <param name="lockToken">锁令牌,用于释放锁</param>/// <param name="lockExpirySeconds">锁自动超时时间(秒)</param>/// <param name="waitLockSeconds">等待锁时间(秒)</param>/// <returns>加锁成功</returns>publicboolLock(string key,out string lockToken,int lockExpirySeconds=10,double waitLockSeconds=0){int waitIntervalMs=1000;string lockKey=GetLockKey(key);DateTime begin=DateTime.Now;string uuid=Guid.NewGuid().ToString();//循环获取取锁while(true){string result;using(varclient=GetNativeClient()){//返回SET操作结果,为OK时成功result=client.EvalStr(ScriptSetIfAbsent,1,System.Text.Encoding.UTF8.GetBytes(lockKey),System.Text.Encoding.UTF8.GetBytes(uuid),System.Text.Encoding.UTF8.GetBytes(lockExpirySeconds.ToString()));}if(result=="OK"){lockToken=uuid;returntrue;}//超过等待时间,则不再等待if((DateTime.Now-begin).TotalSeconds>=waitLockSeconds)break;Thread.Sleep(waitIntervalMs);}lockToken=null;returnfalse;}
/// <summary>/// 释放锁,执行完代码以后调用/// </summary>/// <param name="key">锁Key</param>/// <param name="lockToken">锁令牌</param>/// <returns>释放锁成功</returns>publicboolDelLock(string key,string lockToken){if(string.IsNullOrWhiteSpace(lockToken)){thrownewException("参数lockToken不能为空");}string lockKey=GetLockKey(key);using(varclient=GetNativeClient()){//返回删除的行数,为1时成功string result=client.EvalStr(ScriptDeleteIfEqualValue,1,System.Text.Encoding.UTF8.GetBytes(lockKey),System.Text.Encoding.UTF8.GetBytes(lockToken));returnresult=="1";}}

锁使用过程

if(RedisManager.Lock(key,out tokenLock)){try{IRedisClient rdsclient=null;try{}finally{rdsclient?.Dispose();}}finally{RedisManager.DelLock(key,tokenLock);}}
23    2024-10-07 10:07:31    线程 代码 分布式