Redis机制-Redis互斥锁、分布式锁

目录

[一 互斥锁](#一 互斥锁)

[二 分布式锁](#二 分布式锁)

Redis实现分布式锁

redisson实现分布式锁

可重入性:

主从一致性(性能差):


一 互斥锁

假设我们现在有一个业务要实现秒杀优惠券的功能,如果是一个正常的流程,线程之间应该是这样运作的。

线程1先查询优惠券,如果库存充足,那么扣减库存,然后线程2来查询优惠券信息,如果充足,那么就扣减库存,但是这只是理想的情况。

那么如果出现图2的这种情况呢?

线程1在查询优惠券信息的时候发现库存是充足的,线程2查询的时候也是充足的,他俩都可以进行减库存的操作,假如优惠券只剩1张了,那么谁得到这个优惠券呢?这就是业务中可能出现的问题。

此时就需要用互斥锁来解决问题了

如图,假如线程1在获取互斥锁以后,线程2来了之后就只能发起获取锁的请求,只有当线程1操作完了之后释放锁,线程2获取到锁,才可以进行接下来的操作,这样就不会出现库存超卖的情况。

但是还有一个问题,上面所说的这种情况是针对单个Redis服务器进行加锁的,但是实际的业务逻辑中可能会有多个用户,访问多个Redis服务器,那么这时候要怎么解决呢?

二 分布式锁

假设我的代码部署到了多个tomcat中,每一个tomcat操控一个JVM,此时8080的虚拟机知道8081的线程1也在同时访问优惠券信息吗?这显然是不知道的。

此时就需要用到一个独立于tomcat之外的分布式锁来进行判断

如图,当8080的线程1进行访问时,其余的端口和线程都不可以进行访问,此时就达到了分布式锁的效果,有效的解决了超卖问题。要注意,分布式锁是加在Redis上的,不是自己的代码中。

Redis实现分布式锁

Redis实现分布式锁主要是利用Redis的setnx命令

java 复制代码
SET LOCK VALUE NX EX 10  //加锁
DEL key                  //释放锁

第一条代码是加锁并且给锁设置过期时间,第二行代码是删除锁。如果不给锁设置过期时间,那么就有可能产生死锁的现象。即线程1执行时间过长,线程2一直在等待锁释放。

redisson实现分布式锁

redisson中也有分布式锁的实现

java 复制代码
RLock lock = redissonClient.getLock("lock");
try{
    boolean isLock = lock.tryLock(10,TimeUnit.SECONDS);
    if(isLock){
        System.out.println("执行业务");
    }
} finally{
    lock.unlock();
  }

首先,线程1获取到锁之后,然后去操作Redis,也会去通知看门狗系统,而看门狗系统会每隔释放时间(redisson默认30秒)/3去给锁续期,希望业务完成之前不要因为锁到期而引发线程安全问题,此时线程1执行完,线程2获取到锁就可以继续执行了。而加锁和设置过期时间都是基于lua脚本完成的,这样可以保证操作的原子性。

可重入性:

假设下图是线程1所执行的代码,在redis当中会按照哈希结构去进行存储,key为锁的key,value会存储一个键值对,键为线程名称,value为锁的次数默认为0,加一次锁就+1,释放一次锁就-1.

|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| | |

如上图,执行add1( )时,Thread1第一次加锁时,value会被改成1,当add2( )想获取锁时,此时Redis会进行判断,你是不是线程1来的,发现add2( )的线程名为Thread1,那么此时该锁可以重入的,value值会变成2,当add2( )执行完以后,释放锁,value又变为1,add1( )执行完以后,value变成0,此时的锁才是真正被线程1所释放了。

主从一致性(性能差):

如图,如果是在集群模式下的Redis服务器势必会有主节点和从节点,当线程1过来获取到一把锁时,主节点刚好宕机了,集群又重新选了一个主节点,线程2此时又获取到了和线程1同样的一把锁,这样就会产生锁冲突的现象。

针对这样的情况就要加红锁(RedLock):在多个Redis实例上加锁,而不是只在一个节点上进行加锁,这样就可以避免锁重复。

但是这样实现相对来说比较复杂,因此AP思想和CP思想(zookeeper)可以解决该问题。

所以说性能和复杂是负相关的。

相关推荐
Elastic 中国社区官方博客4 分钟前
使用 Elastic Observability 和 MCP 的 Agentic 驱动 Kubernetes 调查
数据库·elasticsearch·搜索引擎·云原生·容器·kubernetes·全文检索
阿正的梦工坊6 分钟前
DOCKER_DATABASE_URL 逐段解析:部署时候的信息解析
数据库·docker·容器
倒流时光三十年9 分钟前
PostgreSQL 大表字段扩长度 -- 会不会锁表?
数据库·postgresql
彭于晏Yan10 分钟前
Spring Boot 整合 WebSocket + Redis 实现离线消息(三)
spring boot·redis·websocket
Irene199112 分钟前
(AI总结版)完整操作流程:从零配置 Oracle 21c XE 开发环境(安装 CO 示例、安装 SCOTT 教学示例)
数据库·oracle
Han.miracle14 分钟前
Spring Cloud + Nacos 环境切换与配置管理最佳实践
数据库·spring boot·spring cloud·maven
p@nd@16 分钟前
DM删除用户后的不完全恢复测试
数据库·达梦数据库·备份还原·备份恢复
在屏幕前出油18 分钟前
08. ORM——快速开始
数据库·后端·python·sql·pycharm·orm
lzhdim21 分钟前
SQL 入门 11:日期时间格式化、IF、CASE的使用
数据库·sql
juniperhan22 分钟前
Flink 系列第15篇:Flink 侧输出(Side Output)详解及实践
java·大数据·分布式·flink