redis里面存储库存
程序里面减
多并发可能会有问题(超卖)
可以加锁解决(Sychronized),但是只能解决单机问题,在分布式多机器上用谨慎,很可能出BUG
前台Nginx,后台代码部署于多个tomcat,共用redis
多台机器,并发越多,超卖的情况就越明显
setnx key value,当且仅当key不存在,否则会使用第一次的值,后续不会覆盖
利用这个特性去实现锁
因为redis核心命令执行是单线程,库存计算需要存redis,多次进来需要排队,根据是否存在指定key去判断
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent("lockKey", "aaa");
//用返回结果去判断锁
if(!result){
return "系统正在执行"
}
// 执行业务逻辑
// 注意删除锁
stringRedisTemplate.delete("lockKey")
加try catch,删除放到finaly
try{
// 执行业务逻辑
}finally{
stringRedisTemplate.delete("lockKey")
}
锁,加过期时间
// 不要这样分开执行,因为宕机无时不在
stringRedisTemplate.expire("lockKey", 10, TimeUnit.SECONDS);
// 使用元执行
stringRedisTemplate.opsForValue().setIfAbsent("lockKey", "aaa", 10, TimeUnit.SECONDS);
线程运行时间无法预料,会出现下面的问题
逻辑执行一半,过期时间到时,第二次请求又会进去,但是第一次把第二次的锁删了,第三次请求又会进去,第二次又将锁删除
问题根本点:自己的锁,被别的请求删除
解决:每一个锁的值用唯一值及进行标识,删除时做判断
// 设置锁,值为唯一值
String clientId = UUID.randomUUID().toString();
stringRedisTemplate.opsForValue().setIfAbsent("lockKey", clientId, 10, TimeUnit.SECONDS);
// 删除锁,进行值判断
if(clientId.equals(stringRedisTemplate.opsForValue().get("lockKey"))){
// 这儿又可能出现锁到时问题,也会有并发问题
stringRedisTemplate.delete("lockKey")
}
当一个请求过来,加锁成功,开始执行代码逻辑;
这时,在后台开启一个分线程,进行一个定时任务,定时给锁续命;
检查主线程锁是否被删掉,如果删掉了说明任务执行完成了,否则锁快到过期时间时,去延长锁的超时时间
jedis儿子
redisson github地址
引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifacId>redisson</artifacId>
<version>3.6.5</version>
</dependency>
配置
@Bean
public Redisson redisson(){
// 此为单机模式
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(8);
return (Redisson) Redisson.create(config);
}
使用
@Autoried
private Redisson redisson;
// 获取一把锁对象
RLock redissonLock = redisson.getLock("lockKey");
// 加锁
redissonLock.lock();
try{
// 执行业务逻辑
}finally{
// 删锁
redissonLock.unlock();
}
上面的实现,单机没问题。但是多机,在主从、分布式的情况下,会有问题
方案一:
zookeeper,主从节点,主leader,从flowwer,遵循CAP,满足一致性
存到主节点,还要同步到子节点,才能生效
redis遵循AP,满足可用性
存则立马生效
redis性能要比zookeeper强很多,但是zookeeper基本不会丢锁
方案二:
Redlock(红锁)
同zookeeper差不多,也需要将加锁key存到多个节点,只有超过半数节点返回添加成功,加锁才成功
这样,新的加锁请求如果存在问题,则不会超过半数,则不会加锁成功
尽量自己用一套分布式锁服务
问题1:高可用问题
不要通过主从节点去处理,同步的时候就可能有问题。新加锁,因为有子节点,所以半数的计算还是会有多并发问题
高可用,可以多加几个节点,防止节点被挂问题,一般用3-5个节点,注意用奇数节点,别用偶数
问题2:持久化问题
aof持久化方案,1s中一次,依然会丢锁
如果第二个节点不够1s,但是节点挂了,但是重启数据就会掉了,后续的就可以再在这个节点加锁
aof,百分百不丢锁不可能
如果用aways,每一条命令持久化一次,但是性能又太差了
在redis中是将并行转串行实现,但是特别高的并发,性能较低
让串行执行的数量越小越好
ConcurrentHashMap底层实现
在redis中分段存储
P_101 = 200
转化为
P_101_1 = 20
…
P_101_10 = 20
每一个段位都加一个锁
通过分发算法,去请求不同的redis,可以去进行多次并发打到不同请求
这样有多少个段位,就相当于提升了多少倍
快速多次点击提交订单
多个tab(浏览器)点击提交订单
通过分布式锁处理,key设为(userId + 购物车商品id排序(id1+id2+id2+…))
马上过期的时候支付,支付成功了,但是后台取消了
通过分布式锁处理,支付的时候加一把锁(针对订单id),取消的时候加一把锁(针对订单id),key一样,则不会执行业务。
在Redis中执行,具备原子性
JAVA开发的压测软件,模拟多并发