我们来学redis -- redis锁

redis锁

锁场景

单个服务

  • 早年系统使用范围小,用户量少,单个服务足够支持业务
  • 此时,JVM实例并发场景中,使用对象锁实现逻辑有序性
  • 如今单体服务肉眼可见的消失殆尽,早已进入多节点负载时代
  • 此时,每个JVM实例中的对象锁只能控制自己内部多线程的逻辑顺序
  • 多个JVM中各拿各的锁,等于没有控制

单节点Redis

  • 不管是多个线程,还是多个服务节点的线程,问题是只能有一把锁
  • 正好项目中使用了redis,正好可以让redis管理这把锁
  • 在理想环境下,多线程的逻辑有序性再次得到了保证,业务又可以快乐的玩耍了
  • 在不理想环境下,单redis节点故障,拿不到锁,业务 Game Over

多节点Redis

  • 买车都还给个备胎,谁还没个备胎呢
  • 多节点Redis也便是情理之中
  • 节点A故障,节点B继续提供锁服务
  • 慢着,一种不妙的感觉出现了
  • 本来是想着一把锁,这下可不是每个Redis节点可以创建锁了
  • 并发的业务服务向着不同Redis节点发起锁请求,这下都能拿到锁了
  • 这玩着玩着,又Game Over了

发展过程

Redis分布式锁的实现并非一蹴而就,它经历了几个关键的技术迭代,每个迭代都旨在解决前一个方案暴露出的缺陷。

基础版

  • 利用SETNX的互斥性(仅当键不存在时设置成功)来实现锁的互斥获取
  • 使用DEL命令删除该键以释放锁
  • 防止客户端崩溃导致死锁,会配合EXPIRE命令为锁设置一个过期时间(TTL)
  • SETNX和EXPIRE是两条独立的Redis命令,执行不是原子的

原子操作版

  • Redis 2.6.12版本后
  • SET命令支持了NX(仅当不存在时设置)、PX(毫秒级过期)和EX(秒级过期)选项
  • 单命令SET with NX & PX/EX
  • 加锁和设置过期时间能在一个原子操作中完成
  • SET lock_key unique_value NX PX 30000
  • unique_value唯一标识符,可使用UUID或客户端ID,用于在释放锁时验证锁的持有者,防止误删
  • 但是...
  • 这世上的事,就怕个"但是"
    • 服务A持有锁Q,业务执行完了,执行GET,获取值uuid进行检查,是否自己的锁
    • 锁Q超期,系统自动释放锁Q
    • 服务B执行Set ,获取了锁Q
    • 服务A执行Del,删除了锁Q

安全释放版

  • 为了解决非原子性释放的问题,引入了Lua脚本

  • Redis保证Lua脚本的执行是原子性的,不会被其他命令插入

    复制代码
    if redis.call("get", KEYS[1](@ref) == ARGV[1] then
        return redis.call("del", KEYS[1](@ref)
    else
        return 0
    end
  • 将"比较锁持有者标识"和"删除锁"这两个操作封装在一个Lua脚本中

  • 解决了上述"但是"的问题

自动续期与可重入版

  • 如何合理设置锁的过期时间
  • 设置太短,业务未完成锁就过期
  • 设置太长,客户端崩溃后锁释放慢,影响系统可用性
  • Redisson等成熟客户端引入了看门狗线程
  • 启动一个后台守护线程,定期(默认在锁过期时间的1/3处)检查业务是否还在执行
  • 如果是,则自动调用PEXPIRE命令为锁续期

高可用版

  • 以上所有方案都基于单个Redis实例(或主从架构)
  • 为了应对单点故障,Redis作者提出了RedLock算法
  • 客户端向N个(通常为5个)完全独立、无主从关系的Redis主节点依次请求加锁
  • 只有当客户端在超过半数(N/2 + 1) 的节点上加锁成功,且总耗时小于锁的有效时间,才认为加锁成功
  • 核心思想是多数派(Quorum)投票
  • 借鉴了分布式共识中"只要多数节点同意,系统就能做出决定并容忍少数节点故障"的思想
  • 本身不是一个完整的共识算法(如Paxos、Raft),因为它不解决日志复制和状态机一致性问题
相关推荐
让我上个超影吧2 小时前
天机学堂——BitMap实现签到
java·数据库·spring boot·redis·spring cloud
Carry灭霸2 小时前
【BUG】Redisson Connection refused 127.0.0.1
java·redis
无限码力2 小时前
华为OD技术面真题 - 数据库Redis - 1
redis·华为od·华为od技术面真题·华为od技术面八股·华为od技术面八股文·华为od技术面redis问题
想搞艺术的程序员3 小时前
架构破局 - Redis 不再做缓存!替代 MySQL 做主存储
redis·缓存·架构
码农水水3 小时前
米哈游Java面试被问:Shenandoah GC的Brooks Pointer实现机制
java·开发语言·jvm·spring boot·redis·安全·面试
heartbeat..3 小时前
Redis Cluster (Redis 集群模式)从入门到精通:架构解析、机制详解与运维排查
java·运维·redis·架构·nosql
澄风4 小时前
Redis ZSet+Lua脚本+SpringBoot实战:滑动窗口限流方案从原理到落地
spring boot·redis·lua
醒过来摸鱼5 小时前
redis源码deps目录
数据库·redis·缓存
Huanlis5 小时前
Redis Stream 核心原理与实战指南
数据库·redis·缓存
Geoking.5 小时前
【Redis】Redis 中的 Pipeline 与 Lua 脚本:高性能与原子性的两种武器
redis·lua