会会大厂面试官四-----Redis-Springboot+redisson【实现高并发超买超卖,解决9大bug】

73 篇文章 8 订阅
8 篇文章 0 订阅

一、Redisson【面试复盘】

1.1 Redis除了做缓存,你还见过Redis的什么用法?

1.2 Redis做分布式锁有时候需要注意神魔问题?

1.3 如果是Redis单点部署的,会带来神魔问题?

1.4 集群模式下,比如主从模式。会又什么问题?

1.5 简单介绍下Redlock吧,看你简历上有redissson?

1.6 Redis分布式锁如何续期?看门狗知道吗?

二、Redis分布式锁

1.JVM层面的锁
2.分布式微服务架构,拆分后各个微服务之间为了避免冲突和数据故障而加入的一种锁。
3.显示方案:zookeeper mysql redis【推荐】–redlock----redisson lock/unlock

三、超卖程序采坑案例【Springboot2+Redis5/6】

使用场景:多个服务之间,同一时刻,同一个用户只能有一个请求,防止关键业务数据冲突和并发错误。

3.1 建立Module

  • boot_redis01
  • boot_redis02
  • boot_redis_test

3.2 修改pom

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-pools</artifactId>
</dependency>
<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
	<version>3.1.0</version>
</dependency>

3.3 修改yaml

# 第一个模块的配置,第二个修改端口号就好2222
server.port=1111
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0

3.4 RedisConfig配置类

@Configuration
public class RedisConfig{

	@Bean
	public RedisTemplate<String,Serializable> redisTemplate(lettuceConnectionFactory connectionFactory){
		new RedisTemplate<String,Serializable> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(connectionFactory);
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
		
		return redisTemplate;
	}
}

3.5 GoodController

@RestController
public class GoodController{
	
	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	@Value("${server.port}")
	private String serverPort;

	@GetMapping("/buy_Goods")
	public String buy_Goods(){
		// 1.查看库存数量
		String result = stringRedisTemplate.opsForValue().get("goods:001");
		int goodsNumber = result == null ? 0 : Interger.parseInt(result);
		
		// 2.卖商品
		if(goodsNumber > 0){
			int realNumber = goodsNumber - 1;
			// 3.成功买入,库存减少一件
			stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
			return "成功买入商品,库存还剩下:"+realNumber+"服务端口:"+serverPort;
		}else{
			System.out.println("商品卖完"+"服务端口:"+serverPort);
		}
		return "商品卖完!"+"服务端口:"+serverPort;
	}

}

3.6 Redis数据

# 放入001库存100个
set goods:001 100

3.7 测试

在这里插入图片描述
在这里插入图片描述

四、找上面程序Bug

4.1 高并发下,又什么问题?

单机版没有枷锁100%故障的,没有原子性,多线程下没有枷锁是不可以的。

4.1.1 单机版加synchronized锁

关键字,拿不到商品不走,容易造成线程积压,卡在外面,时间比较久。

@GetMapping("/buy_Goods")
	public String buy_Goods(){
	
	// 加锁
	synchronized(this){
		// 1.查看库存数量
		String result = stringRedisTemplate.opsForValue().get("goods:001");
		int goodsNumber = result == null ? 0 : Interger.parseInt(result);
		
		// 2.卖商品
		if(goodsNumber > 0){
			int realNumber = goodsNumber - 1;
			// 3.成功买入,库存减少一件
			stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));
			return "成功买入商品,库存还剩下:"+realNumber+"服务端口:"+serverPort;
		}else{
			System.out.println("商品卖完"+"服务端口:"+serverPort);
		}
		return "商品卖完!"+"服务端口:"+serverPort;
	}
}
4.1.1 单机版加ReentrantLock锁

类 ,try lock,时间内抢的到就去抢,抢不到就走人。


private final Lock lock = new ReentrantLock();

@GetMapping("/buy_Goods")
	public String buy_Goods(){
	
	// 加锁,抢锁3S小规模等待
	try(lock.tryLock(3L,TimUnit.SECONDS)){
		lock.lock();
		...
	}finally{
		lock.unlock();
	}else{
	}
}

4.2 架构变为Nginx分布式微服务,单机锁会不会有问题?

在这里插入图片描述
单机版的解决不了的,需要加入分布式锁

4.2.1 引入反向代理Nginx
# 权重一半一半
vi nginx.conf

在这里插入图片描述
测试访问nginx

192.168.11.147/buy_goods

似乎轮询策略没有发现神魔问题。

4.2.2 Jmeter性能压测会不会有问题?

压测:1S中100个线程进行并发访问
在这里插入图片描述
在这里插入图片描述
结果:发现了严重的超卖现象,单机版的锁是控制不住问题的。
在这里插入图片描述
Redis性能极高,岁分布式锁的支持比较优秀。

4.2.3 加入分布式锁
public static final String REDIS_LOCK = "atguiguLock";

@GetMapping("/buy_Goods")
	public String buy_Goods(){
		String value = UUID.randomUUID().toString()+Thread.currentThread().getName();	
		
		Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK,value);
		
		// 加锁不成功
		if(!flag){
			return "抢锁失败";
		}
		
		String result = stringRedisTemplate.opsForValue().get("goods:001");
		int goodsNumber = result == null ? 0 : Interger.parseInt(result);
		
		// 2.卖商品
		if(goodsNumber > 0){
			int realNumber = goodsNumber - 1;
			// 3.成功买入,库存减少一件
			stringRedisTemplate.opsForValue().set("goods:001",String.valueOf(realNumber));

			// 4.解锁
			stringRedisTemplate.delete(REDIS_LOCK);
			
			return "成功买入商品,库存还剩下:"+realNumber+"服务端口:"+serverPort;
		}else{
			System.out.println("商品卖完"+"服务端口:"+serverPort);
		}
		return "商品卖完!"+"服务端口:"+serverPort;
}

4.3 程序异常,没有执行解锁命令怎么办?

出异常可能没有办法释放锁,必须要加入一个finally代码块,用来释放锁。
在这里插入图片描述

4.4 部署了微服务的jar包机器挂了,代码没有执行finally怎么办?

key没有被删除,麻烦了,redis中一直有这把锁,需要加入一个过期删除。

Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK,value);
// 保证释放锁【过期时间的限定】
stringRedisTemplate.expire(REDIS_LOCK,10L,TimeUnit.SECONDS);

4.5 加锁和设置KEY分开了,没有保证原子性?

Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK,value,10L,TimeUnit.SECONDS);

4.6 时间过期了,第一个线程业务还没完成怎么办?

程序不严谨带来隐患,第一个线程超过了过期删除的时间限制,删除了别人的锁。
在这里插入图片描述
只能删除自己的锁,不能删除别人的锁。
在这里插入图片描述

4.7 finally块的判断+del删除操作不是原子性的?

redis官网的解决办法
在这里插入图片描述

4.7.1 如果不可以使用lua脚本,你还有其他办法吗? 【RedisTemplate版本】

提示,redis自身的事务来解决。

在这里插入图片描述
4

4.7.2 lua脚本来解决【Jedis+lua】

在这里插入图片描述
在这里插入图片描述

4.8 Redis分布式锁如何进行缓存续命?

确保你的业务逻辑时间 > 过期时间

五、 Redisson的实现

5.1 RedisConfig

@Bean
public Redisson redisson(){
	Config config = new Config();
	config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);
	return (Redisson)Redisson.create(config);
}

5.2 GoodsController

@Autowired
private Redisson redisson;

@GetMapping("/buy_Goods")
	public String buy_Goods(){
		String value = UUID.randomUUID().toString()+Thread.currentThread().getName();	
		
		RLock redissonLock = redisson.getLock(REDIS_LOCK);
		redissonLock.lock();
		
		...
		finally{
			redisson.unlock();
		}

5.3 nginx+jmeter+redisson性能压测

在这里插入图片描述
通过查询1号机与2号机,并没有查到问题。基本解决了超卖问题。
细节弥补,为了解决解锁时候不是解决的自己的锁问题。 做一个判断。
在这里插入图片描述

  • 9
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coding路人王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值