Java面试专题-redis篇分布式锁
二、分布式锁
1.使用场景
- redis分布式锁,是如何实现的?
需要结合项目中的业务进行回答,通常情况下,分布式锁使用的场景:
集群情况下的定时任务、抢单、幂等性场景
例:抢券场景:
/ **
*抢购优惠券
*@throws InterruptedException
*/
public void rush ToPurchase() throws InterruptedException {
//获取优惠券数量
Integer num = (Integer) redisTemplate.opsForValue().get("num");
//判断是否抢完
if (null == num || num <= 0) {
throw new RuntimeException(“优惠券已抢完”);
}
//优惠券数量减一,说明抢到了优惠券
num = num - 1;
//重新设置优惠券的数量
redis Template.opsForValue().set("num", num);
}
抢券执行流程:
1.正常情况下:
2.库存只剩1个的情况
超卖情况(库存 -1)
3.先使用synchronied锁
public void rush ToPurchase() throws InterruptedException {
synchronized (this){
//查询优惠券数量
Integer num = (Integer) redisTemplate.opsForValue().get("num");
//判断是否抢完
if (null == num || num <= 0) {
throw new RuntimeException("商品已抢完");
}
//优惠券数量减一(减库存)
num = num - 1;
//重新设置优惠券的数量
redisTemplate.opsForValue().set("num", num);
}
}
如果项目是单体项目,并且只启动了一台服务,这个代码就没有问题。
4.服务集群部署
但是一般为了满足并发请求,会把服务网做成集群部署。
每个服务都有各自的JVM,解决不了多个JVM下线程的互斥
所以在这种情况下,要使用外部的锁来解决——分布式锁
2.实现原理(setnx, redisson)
redis分布式锁:
1.setnx
redis实现分布式锁主要利用redis的setnx命令。setnx是SET if not exists(如果不存在,则SET)的简写。
-
获取锁:
# 添加锁,NX是互斥、EX是设置超时时间 SET lock value NX EX 10-
一条命令保证原子性
-
不设置超时时间可能会导致死锁
-
-
释放锁:
# 释放锁,删除即可 DEL key
-
redis实现分布式锁如何合理的控制锁的有效时长?
根据业务执行时间预估(不太合适)
给锁续期 ---> redisson
2.redisson
加锁成功之后,另开一个线程进行监控
手动释放锁
public void redisLock() throws InterruptedException {
//获取锁(重入锁),执行锁的名称
RLock lock = redissonClient.getLock( s: "heimalock");
//尝试获取锁,参数分别是:获取锁的最大等待时间(期间会重试),锁自动释放时间,时间单位
//boolean isLock = lock.tryLock(10,30, TimeUnit.SECONDS);
boolean isLock = lock. tryLock ( time: 10, TimeUnit. SECONDS);
//判断是否获取成功
if (isLock) {
try {
System.out.println("执行业务”);
} finally {
//释放锁
lock. unlock();
}
}
}
加锁、设置过期时间等操作都是基于lua脚本完成
重入
- redisson实现的分布式锁是否可以重入呢?
可以,要判断是不是同一个线程,是就可重入
public void add 1(){
RLock lock = redissonClient.getLock("heimalock");
boolean isLock = lock.tryLock();
//执行业务
add2();
//释放锁
lock.unlock();
}
public void add2(){
RLock lock = redissonClient.getLock("heimalock");
boolean isLock = lock.tryLock();
//执行业务
//释放锁
lock.unlock();
}
利用hash结构记录线程id和重入次数
| KEY | VALUE | |
|---|---|---|
| field | value | |
| heimalock | thread1 | 2 |
主从一致性
-
redisson实现的分布式锁能保证主从数据的一致吗?
当主节点还没来得及同步数据,他挂了,宕机,那么依据redis的哨兵模式,他会在从节点中选择一个当作主节点
这时候,两个线程同时持有一把锁,不满足互斥性了,可能会出现脏数据
所以,提出红锁
RedLock(红锁):不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁**(n/2+1)**,避免在一个redis实例上加锁。
一般不常使用:实现复杂!性能差!运维繁琐!
redis整体集群是AP思想 (高可用)
如果要保证强一致,建议使用CP思想的zookeeper
总结
- redis分布式锁,是如何实现的?
● 先按照自己简历上的业务进行描述分布式锁使用的场景 ● 我们当使用的redisson实现的分布式锁,底层是setnx和lua脚本(保证原子性)
-
Redisson实现分布式锁如何合理的控制锁的有效时长?
在redisson的分布式锁中,提供了一个WatchDog(看门狗),一个线程获取锁成功以后,WatchDog会给持有锁的线程续期(默认是每隔10秒续期一次)
-
Redisson的这个锁,可以重入吗?
可以重入,多个锁重入需要判断是否是当前线程,在redis中进行存储的时候使用的hash结构,来存储线程信息和重入的次数
-
Redisson锁能解决主从数据一致的问题吗
不能解决,但是可以使用redisson提供的红锁来解决,但是这样的话,性能就太低了,如果业务中非要保证数据的强一致性,建议采用zookeeper实现的分布式锁
开始看黑马Java面试,27届的,大家有什么建议欢迎来说哟
查看18道真题和解析