关于redis分布式锁入门

原理
单一实例下:使用setnx命令(在key不存在时,创建并设置value 返回1,key存在时,会返回0)来获取锁。
应用
以扣库存为例,使用锁能够有效保证不会被超卖;
基础版方案:数据库锁
SELECT stock from goods where ID =1 for update; update goods set stock=stock-1 where id=1 and stock=10 // 正常情况下看你业务需求,如果能够在落库前确认库存的话,上面这句话后面的stock=10能够限定库存锁定值 // 如果不使用这个stock=10的话,会有一定的副作用,比如说常见的,多线程轮扣redis 版v0
使用setnx写入一个键值,如果成功则表明获取到锁,执行业务;对于没有获取到锁的,则会轮询获取。
int waitIntervalMs = 50;//间隔等待时长 毫秒
string lockKey = "lock_key:" + key;
DateTime begin = DateTime.Now;
while (true)
{
if (redisClient.SetNX(lockKey, new byte[] { 1 }) == 1)
{
redisClient.Expire(lockKey, expirySeconds);
return true;
}
//不等待锁则返回
if (waitSeconds <= 0)
break;
if ((DateTime.Now - begin).TotalSeconds >= waitSeconds)//等待超时
break;
System.Threading.Thread.Sleep(waitIntervalMs);
}
return false;
不管是该业务成功、失败或者异常,需要确保该锁在周期结束后能被释放
redis 版v1
V0版本存在的缺陷:比较致命的是可能会出现,任务还未完成,锁就超时了,导致key被回收了,那么能不能将锁的申请时间作为值进行存储呢?
int waitIntervalMs = 200;//间隔等待时长 毫秒 设置成足够长,至少要超过数据库的超时响应时间 string lockKey = "lock_key:" + key; DateTime begin = DateTime.Now; DateTime now = DateTime.Now; while (true) { //在key值存在的情况下 if(redisclient.ExistKey(lockKey)) { //如果key对应的value值+超时时间后小于当前时间,则移除key值,让业务正常运行 if(Convert.ToDateTime(redisclient.GetKey(lockkey).Value)+TimeLimit<now){ redisclient.remove(lockkey); } } if (redisClient.SetNX(lockKey, now.tostring()) == 1) { redisClient.Expire(lockKey, expirySeconds); return true; } //不等待锁则返回 if (waitSeconds <= 0) break;
if ((DateTime.Now - begin).TotalSeconds >= waitSeconds)//等待超时 break;
System.Threading.Thread.Sleep(waitIntervalMs);
} return false; ```
遗留的问题
在单个redis实例下,上述代码工作都ok,如果存在redis集群,且master挂掉的情况,那么这个锁一定锁不住




