【Redis场景1】用户登录注册
【Redis场景2】缓存更新策略(双写一致)
【Redis场景3】缓存穿透、击穿问题
【Redis场景拓展】秒杀问题-全局唯一ID生成策略
【Redis场景4】单机环境下秒杀问题
在单机环境下的并发问题,我们可以使用相关锁来解决;但是在集群环境中,笔者测试通过Nginx
做的反向代理和负载均衡,请求的时候锁会出现失效的问题。
原因:我们部署多个服务(存在多个tomcat
服务器),每个tomcat
都有一个属于自己的jvm
.每个锁在同容器中有效,但是跨容器后就无法实现互斥效果。
引出分布式锁:
一个最基本的分布式锁需要满足:
分布式锁的实现:
SETNX
实现分布式锁GitHub完整代码:https://github.com/xbhog/hm-dianping/tree/20230211-xbhog-redisCloud
锁接口实现:20230211-xbhog-redisCloud
/*** @author xbhog* @describe:* @date 2023/2/16*/
public interface ILock {boolean tryLock(Long timeOutSec);void unLock();
}
加锁解锁实现类:
@Override
public boolean tryLock(Long timeOutSec) {String threadId = ID_PREFIX + Thread.currentThread().getId();Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + keyName, threadId + "", timeOutSec, TimeUnit.SECONDS);//防止拆箱引发空值异常return Boolean.TRUE.equals(isLock);
}
@Override
public void unlock() {//通过del删除锁stringRedisTemplate.delete(KEY_PREFIX + name);
}
现在有两个锁,线程1获取锁时,由于业务的阻塞超时释放了,这是线程2开始操作,获取锁,在线程2执行业务期间,线程1业务在一段时间内不阻塞且业务完成,这是开始执行释放锁的操作,但是这是锁是线程2,由此造成锁的误删问题;
正确流程:
解决的方式:
修改之前的分布式锁实现,满足:在获取锁时存入线程标示(可以用UUID表示) 在释放锁时先获取锁中的线程标示,判断是否与当前线程标示一致
核心逻辑:在存入锁时,放入自己线程的标识,在删除锁时,判断当前这把锁的标识是不是自己存入的,如果是,则进行删除,如果不是,则不进行删除。
处理流程:
代码实现:
private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";
@Override
public boolean tryLock(Long timeOutSec) {String threadId = ID_PREFIX + Thread.currentThread().getId();Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + keyName, threadId + "", timeOutSec, TimeUnit.SECONDS);//防止拆箱引发空值异常return Boolean.TRUE.equals(isLock);
}
@Override
public void unLock() {String threadId = ID_PREFIX + Thread.currentThread().getId();//获取当前分布式锁中的valueString id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + keyName);//锁相同则删除if(threadId.equals(id)){stringRedisTemplate.delete(KEY_PREFIX + keyName);}}