Redis分布式锁

什么是分布式锁
当多个系统对同一数据进行修改时,并且要求这个修改是原子性的,那么就要应用到分布式锁。

分布式锁的应用场景
秒杀时解决库存超卖问题

分布式锁的特点
i)互斥性
任意时刻,只有一个客户端能够持有锁
ii)不会发生死锁
即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端加锁成功
iii)容错性
只要大部人的redis节点正常运行,客户端就可以加锁和解锁
iiii)解锁
加锁和解锁必须为同一个客户端,客户端不能解锁他人的锁

常用redis命令
setnx: key不存在时,为key设置指定值
expire: 设置key的过期时间,单位:秒
getset:设置指定key的值,并返回 key 的旧值
del:用于删除已经存在的键

分布式锁的实践

  1. 错误示例
if(redis->setnx(key, value)){
	redis->expire(key, value)
}

实现思路: 在当前锁没有被占用的情况下,加锁成功后,给锁设置一个过期时间。
但是由于setnx/expire不具有原子性,如果在执行expire前该进程崩溃,则会导致锁永远存在,后续进程在获取锁时发现锁已存在而无法枷锁。

  1. 正确示例
redis->set(key, value, ['nx', 'px'=>expire])

参数说明:
nx: set if not exist , 即当key不存在时,进行set操作,若key已经存在,则不做任何操作
px:设置key的过期时间,单位:ms

  1. 解锁实现思路
    最常见的一种错误解锁方式是直接通过del来进行的
redis->del(key)

这种错误原因是不具有拥有者标识[谁加锁,谁解锁],任何客户端都可以随时解锁

所以在用setnx加锁时,应该给value设置一个唯一的id,或者用uuid这种随机数。当解锁的时候,先获取value,判断是否为当前进程加的锁,再去删除

$uuid = xxxx;
redis->set('test', $uuid,  ['nx', 'px'=>expire])
try{
	
}finally{
	if($uuid === reids->get(key)){
		redis->del(key)
	}
}

注:但是get和del并非原子操作,所以还是会存在进程安全问题

正确解锁步骤,使用lua脚本,通过redis的eval来运行脚本。新建一个unlock.lua

if redis.call('get', KEYS[1])== ARGV[1]
	then
		return redis.call('del', KEYS[1]);
	else	
		return 0;
end

//运行脚本
redis->eval(unlock.lua,  [key, value], 1);

使用脚本的好处
redis 在2.6推出了脚本功能,允许开发人员使用lua语言编写脚本传到redis中执行。
i)原子操作:redis 会将整个脚本作为一个整体执行,中间不会被其他命令插入
ii) 复用:客户端发送的脚本永久储存在redis中,意味着其他客户端可以复用该脚本

参考资料:
redis setnx原子性_面试被问Redis锁??的缺点,被打击的扎心了
nx set 怎么实现的原子性_面试官:谈谈分布式锁的实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值