redis分布式是如何实现的(面试版)

需要结合项目中的业务进行回答,通常情况下,分布式锁使用的场景集群情况下的定时任务、抢单、幂等性场景。

下面先来看一个抢卷场景:

以下情况会出现超卖情况:

因为线程会交替执行,所以线程查询优惠价的数据量后,线程2又查询优惠券的数据量,此时如果优惠券总数是1,最后优惠券的数量会变成-1,出现超卖。

使用synchronized解决:

但在集群的情况下,这种方式(本地加锁)会失效,因为synchronized是本地锁,这个锁属于JVM,每个服务都有各自的JVM,它只能解决同一个JVM下线程的互斥:

此时需要加分布式锁:

redis分布式锁

什么是分布式锁:支持分布式集群环境下的锁

基于redis来实现分布式锁,底层使用的是setnx和lua脚本。

图1-1

上图中,必须设置锁的超时时间,否则当服务宕机的时候就不会释放锁了。

redisson实现的分布式锁如何合理控制锁的有效时长

继续看图1-1,因为加锁的时候给了一个失效时间,假如业务的执行时机太长,已经超出了锁的释放时间,但此时业务还没有执行完成,假如其他线程来获得锁就会获得成功

出现这种情况后,可以给锁续期:redisson实现的分布式锁-执行流程:

  1. 有一个线程(线程1)过来,它获取锁并加锁成功后,然后就会去操作redis
  2. 加锁成功后,会另开一个线程(Watch dog)监控 持有锁的线程给持有锁的线程 增加锁的持有时间**(续期)。续期的规则为每隔 releaseTime/3(releaseTime是锁的过期时间) 的时间**做一次续期,默认为10秒。
  3. 手动释放锁,手动释放锁之后,需要通知对应线程的Watch dog不需要再监听了

图2-1

假如,现在又来了一个新的线程(线程2),想获取锁,它先尝试去加锁,如果加锁成功,和 图2-1的流程是一样的,但如果没有加锁成功呢?在redisson实现的分布式锁中,它设置了一个while循环来不断的尝试去加锁,如果加锁成功了就成功了,如果线程1一直没有释放锁,线程2有一个阈值,只要循环到了阈值,线程2就会获取锁失败,这种机制会在高并发下,大程度增加分布式锁的性能:

下图为redisson的使用,所有的redisson命令基于lua脚本 完成,保证脚本执行的原子性

redisson实现的分布式锁是否可以重入

首先分析下图的代码:

  • 在add1中尝试获取了锁,再去调用add2,add又去尝试获取锁,此时都是同一个线程,可以重入。

回答:可以重入,判断是否是当前线程 ,通过线程id去判断,如果是同一个线程就可重入,否则不能

redisson中可重入的实现

使用hash类型来存储锁数据(线程的唯一标识和重入次数):

  • 每加一次锁,重入次数都会+1,每释放一次锁重入次数-1,全部释放完变成0后删除锁信息。

redisson实现的分布式锁可以保证主从一致吗

不能解决,但可以使用红锁保证主从一致,但性能太低,一般项目中用的不多,哪有服务器天天挂;如果想要保证数据的强一致性就去使用CP思想的zookeeper。

相关推荐
longlongqin7 分钟前
redis的 stream数据类型实现 消息队列?
数据库·redis·缓存
hankl199014 分钟前
spark里使用geohash处理数据之线程安全问题
大数据·分布式·spark
wrx繁星点点20 分钟前
多个线程同时写入一个共享变量,会发生什么问题?如何解决?
java·开发语言·数据库
鲨鱼辣椒ii36 分钟前
sql中索引查看是否生效
数据库·sql
leidata1 小时前
MySQL系列—10.Innodb行格式
数据库·mysql
阿维的博客日记2 小时前
聚簇索引和二级索引
数据库·聚簇索引·二级索引
kingandlog2 小时前
Redis网络模型、通信协议、内存回收
java·网络·redis
璇嘟嘟2 小时前
springboot-创建连接池
数据库
计算机程序设计开发2 小时前
小说阅读网站登录注册搜索小说查看评论前后台管理计算机毕业设计/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序
数据库·vue.js·spring boot·java-ee·课程设计·计算机毕业设计·数据库管理系统