Redis 分布式锁

redis · 浏览次数 : 0

小编点评

Redis 分布式锁是一种基于 Redis 的方式来保证多个节点之间的资源同步访问,避免并发操作导致的数据不一致问题。分布式锁的实现方式有多种,包括基于 setnx 的方式、基于 Lua 脚本的方式等。下面对分布式锁的演变和本地锁(单机用)利用 Redis 进行分布式锁的使用进行归纳总结。 一、本地锁(单机用) 本地锁是指在单个节点上实现的锁,通常使用 Redis 的 set 命令来实现。其基本思路是:当一个节点想要获取锁时,先使用 setnx命令尝试设置锁,如果返回 1,则表示成功获取锁;否则表示锁已被其他节点占用。为了避免因锁过期而导致的死锁问题,可以在设置锁的时候加上过期时间,同时在使用锁的过程中定期续期,以保证锁的有效性。 二、分布式锁 分布式锁是指在多个节点之间实现的锁,以保证多个节点之间的资源同步访问。其基本思路是:在每个节点上创建一个锁,通过 setnx 命令尝试获取锁,如果返回 1,则表示成功获取锁;否则表示锁已被其他节点占用。由于多个节点之间可能同时有多个请求争抢锁,因此需要考虑死锁和重入性问题。一种解决方法是使用 Lua 脚本进行加锁和解锁操作,以保证操作的原子性。另外,为了避免业务执行时间过长导致锁过期,还需要在锁的过期时间到期之前进行续期操作。 三、Redis 分布式锁的优势 相比于其他分布式锁的实现方式,Redis 分布式锁具有以下优势: 1. 实现简单,只需要使用 Redis 的 setnx 和 del 命令即可实现。 2. 性能较高,因为 Redis 是基于内存的数据库,读写速度非常快。 3. 容易实现,只需要在每个节点上部署 Redis 即可。 4. 可以使用 Lua 脚本进行加锁和解锁操作,保证操作的原子性。 5. 可以避免死锁和重入性问题,因为设置了锁的过期时间,并且每次加锁都需要使用 Lua 脚本进行原子操作。 四、Redis 分布式锁的应用场景 Redis 分布式锁广泛应用于需要多个节点之间同步访问资源的场景,例如: 1. 大型网站的高并发访问,如电商网站的购物车、订单结算等功能。 2. 分布式系统的协同计算,如大数据处理、人工智能等领域。 3. 微服务架构中的服务注册与发现,需要保证同一服务实例之间不会发生资源竞争。 4. 实时消息队列的消息订阅与发布,需要保证同一主题的消息能够被同一节点的消费。 总之,Redis 分布式锁是一种非常实用且高效的分布式锁实现方式,可以广泛应用于各种需要保证多节点之间资源同步访问的场景。

正文

Redis 分布式锁

分布式锁的演变

  1. 本地锁(单机用)
  2. 利用redis进行分布式锁 使用 set
  3. 防止死锁 加过期时间 使用 setnx
  4. 防止A请求未执行完 锁过期删除 B请求加锁后 A完成后误删该锁 使用 Hash结构, 规定每个请求只能删除自己的锁
  5. 保证并发安全,申请锁和加过期时间需要 原子性,用 lua脚本 加锁或解锁
  6. 考虑到 重入性 (每个请求只拿到一个锁后,可以多个函数或线程共用) 使用 Hash结构进行加减(hincrby) 操作
  7. 为了保证业务执行过长,锁不会过期。需要对锁进行 续期

1. setNX

  • 注意死锁
  • 注意lock过期时间和业务执行时间
  • 一般情况下完全够用

2. 考虑look重入性

  • 当一个锁被创建出来之后再同一个请求中不需要再额外申请其他锁,一个锁可以被重复使用,所以使用到 Hash数据结构的锁
  • 如果业务上需要应用到多个函数的锁的情况下,申请一个锁之后固定当前请求的uuid。入一个函数(或者线程)需要用到时 向当前锁的uuid +1
  • 直到请求结束后,检查线程锁是否归0 如果是释放该锁,如果不是说明其他线程未能执行完毕。
锁的操作保证原子性应对高并发
  • 当一个锁(Hash结构) 创建锁和增加过期时间,两步需要lua脚本进行执行保证原子性。
// lua 脚本编写 
// 加锁操作  如果锁不存在或者当前请求锁的uuid字段存在 则进行加一 (重入性)

KEYS[1]  //  -- 分布式锁的key
ARGV[1]  //  -- 锁的唯一标识,通常是线程ID或调用者标识
ARGV[2]  //  -- 锁的过期时间,单位为毫秒

if redis.call('exists', KEYS[1]) == 0 //锁不存在
or redis.call('hexists', KEYS[1], ARGV[1]) == 1 // 当前请求有锁 进入函数(或新开线程)无需额外申请锁
then
  redis.call('hincrby',KEYS[1], ARGV[1], 1)
  redis.call('expire',KEYS[1], ARGV[2])
  return true
else
  return false
end

//解锁操作
  if redis.call("hexists",KEYS[1], ARGV[1]) == 0 then // 无锁  无需解锁
      return false
  else if redis.call("hincrby",KEYS[1], ARGV[1], -1) == 0 then  // 锁存在且是自己请求的锁 进行减一操作,如果减为0 则解锁(删key)
      retrun redis.call("DEL",KEYS[1])
  else
      return 0
  • 当lock 创建时,需要同步启动一个定时续期任务,锁存在并过该定时时间进行续期,防止业务未完直接释放锁。
  • 当主线程执行完业务流程 并释放锁之后,续期机制同时结束。

与Redis 分布式锁相似的内容:

Redis 分布式锁

Redis 分布式锁 分布式锁的演变 本地锁(单机用) 利用redis进行分布式锁 使用 set 防止死锁 加过期时间 使用 setnx 防止A请求未执行完 锁过期删除 B请求加锁后 A完成后误删该锁 使用 Hash结构, 规定每个请求只能删除自己的锁 保证并发安全,申请锁和加过期时间需要 原子性,

redis分布式锁,setnx+lua脚本的java实现

本文是基于redis缓存实现分布式锁,其中使用了setnx命令加锁,expire命令设置过期时间并lua脚本保证事务一致性。Java实现部分基于JIMDB提供的接口。

【进阶篇】基于 Redis 实现分布式锁的全过程

这一篇文章拖了有点久,虽然在项目中使用分布式锁的频率比较高,但整理成文章发布出来还是花了一点时间。在一些移动端、用户量大的互联网项目中,经常会使用到 Redis 分布式锁作为控制访问高并发的工具。

图解Redis和Zookeeper分布式锁

使用Redis还是Zookeeper来实现分布式锁,最终还是要基于业务来决定,可以参考以下两种情况: (1)如果业务并发量很大,Redis分布式锁高效的读写性能更能支持高并发 (2)如果业务要求锁的强一致性,那么使用Zookeeper可能是更好的选择

[转帖]【Redis学习06】分布式锁及其优化

文章目录 前言1. 什么是分布式锁2. 分布式锁的实现2.1 基于Redis的分布式锁实现方法2.2 基于redis实现分布式锁的初级版本2.3 改进分布式锁2.4 基于Lua脚本改善分布式锁 前言 上一篇博客我们讲到秒杀问题的一人一单在单机模式下使用synchronized添加悲观锁能解决并发问题

[转帖]【Redis学习06】分布式锁及其优化

文章目录 前言1. 什么是分布式锁2. 分布式锁的实现2.1 基于Redis的分布式锁实现方法2.2 基于redis实现分布式锁的初级版本2.3 改进分布式锁2.4 基于Lua脚本改善分布式锁 前言 上一篇博客我们讲到秒杀问题的一人一单在单机模式下使用synchronized添加悲观锁能解决并发问题

[转帖]【Redis学习06】分布式锁及其优化

文章目录 前言1. 什么是分布式锁2. 分布式锁的实现2.1 基于Redis的分布式锁实现方法2.2 基于redis实现分布式锁的初级版本2.3 改进分布式锁2.4 基于Lua脚本改善分布式锁 前言 上一篇博客我们讲到秒杀问题的一人一单在单机模式下使用synchronized添加悲观锁能解决并发问题

Redis系列13:分布式锁实现

[Redis系列1:深刻理解高性能Redis的本质](https://www.cnblogs.com/wzh2010/p/15886787.html "Redis系列1:深刻理解高性能Redis的本质") [Redis系列2:数据持久化提高可用性](https://www.cnblogs.com/w

RabbitMQ+redis+Redisson分布式锁+seata实现订单服务

引言 订单服务涉及许多方面,分布式事务,分布式锁,例如订单超时未支付要取消订单,订单如何防止重复提交,如何防止超卖、这里都会使用到。 开启分布式事务可以保证跨多个服务的数据操作的一致性和完整性, 使用分布式锁可以确保在同一时间只有一个操作能够成功执行,避免并发引起的问题。 订单流程(只展示重要的内容

缓存面试解析:穿透、击穿、雪崩,一致性、分布式锁、Redis过期,海量数据查找

本文提供了一些保证数据一致性和设计分布式锁的策略。这些策略可以在实际应用中帮助开发人员解决相关的问题,确保系统的数据一致性和并发访问的正确性。同时,通过合理地使用缓存和分布式锁,可以提高系统的性能和可靠性。希望对你在面对Redis相关面试题时有所帮助!