redis实现分布式锁

redis分布式锁使用场景

例子一

比如:一个订单,客户正在前台修改地址,管理员在后台同时修改备注。地址和备注字段的修改,都必须正确更新,这二个请求同时到达的话,如果不借助db的事务,很容易造成行锁竞争,但用事务的话,db的性能显然比不上redis轻量。

解决思路:A,B二个请求,谁先抢到分布式锁(假设A先抢到锁),谁先处理,抢不到的那个(即:B),在一旁不停等待重试,重试期间一旦发现获取锁成功,即表示A已经处理完,把锁释放了。这时B就可以继续处理了。

例子二

上传文件操作,创建文件目录,两个请求,谁先抢到锁就先判断目录是否存在,不存在则创建。

redis的setnx命令

在这里插入图片描述

Redis分布式锁可能存在一些问题:

  1. 设置过期时间
    A客户端获取锁成功,但是在释放锁之前崩溃了,此时该客户端实际上已经失去了对公共资源的操作权,但却没有办法请求解锁(删除 Key-Value 键值对),那么,它就会一直持有这个锁,而其它客户端永远无法获得锁。
    在加锁时为锁设置过期时间,当过期时间到达,Redis 会自动删除对应的 Key-Value,从而避免死锁。
  2. SETNX 和 EXPIRE 非原子性
    如果SETNX成功,在设置锁超时时间之前,服务器挂掉、重启或网络问题等,导致EXPIRE命令没有执行,锁没有设置超时时间变成死锁。Redis 2.6.12 之后 Redis 支持 nx 和 ex 操作是同一原子操作。
  3. 锁误解除
    如果线程 A 成功获取到了锁,并且设置了过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁;随后 A 执行完成,线程 A 使用 DEL 命令来释放锁,但此时线程 B 加的锁还没有执行完成,线程 A 实际释放的线程 B 加的锁。
    通过在 value 中设置当前线程加锁的标识,在删除之前验证 key 对应的 value 判断锁是否是当前线程持有。可生成一个 UUID 标识当前线程

下图是使用SpingBoot集成Redis后使用分布式锁:
spring实现redis分布式锁
4. 超时解锁导致并发
如果线程 A 成功获取锁并设置过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁,线程 A 和线程 B 并发执行。
一般有两种方式解决该问题:
将过期时间设置足够长,确保代码逻辑在锁释放之前能够执行完成。
为获取锁的线程增加守护线程,为将要过期但未释放的锁增加有效时间。

redis分布式锁:redLock

红锁 (redLock) 并非是一个工具,而是redis官方提出的一种分布式锁的算法。

在redisson中,就实现了redLock版本的锁。也就是说除了getLock方法,还有getRedLock方法。

在这里插入图片描述
redLock算法虽然是需要多个实例,但是这些实例都是独自部署的,没有主从关系。

回到上面那张简陋的图片,红锁算法认为,只要2N + 1个节点加锁成功,那么就认为获取了锁, 解锁时将所有实例解锁。 流程为:

  1. 顺序向五个节点请求加锁
  2. 根据一定的超时时间来推断是不是跳过该节点
  3. 三个节点加锁成功并且花费时间小于锁的有效期
  4. 认定加锁成功

也就是说,假设锁30秒过期,三个节点加锁花了31秒,自然是加锁失败了。

这只是举个例子,实际上并不应该等每个节点那么长时间,就像官网所说的那样,假设有效期是10秒,那么单个redis实例操作超时时间,应该在5到50毫秒(注意时间单位)。

还是假设我们设置有效期是30秒,图中超时了两个redis节点。 那么加锁成功的节点总共花费了3秒,所以锁的实际有效期是小于27秒的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值