(Redis秒杀案例(基于springboot))
以下代码块虽然解决了,超卖问题(同时有多个进程来,会导致数据不同步),但是在分布式环境下,并发量高的话仍然又超卖问题。
@RestController
public class indexController {
@Autowired
private Redisson redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct_stock")
public String deductStock(){
synchronized(this){
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if(stock > 0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock",realStock + " ");
System.out.println("扣减成功,剩余库存:"+realStock);
}else {
System.out.println("扣减失败,库存不足");
}
}
return "end";
}
}
解决高并发场景下的超卖问题,除了解决不了宕机问题,还有就是在高并发场景下导致进程会错误删除其他进程的锁,导致锁的失效,也会引起超卖问题
@RestController
public class indexController {
@Autowired
private Redisson redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct_stock")
public String deductStock(){
String lockKey = "lockKey";//简单的不完善的分布式状态锁
try {
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent("lockKey", "zhujian");//jedis.setnx(key,value)
if(!result){
return "error_code";//前端根据错误状态码,做出友好提示
}
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if(stock > 0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock",realStock + " ");
System.out.println("扣减成功,剩余库存:"+realStock);
}else {
System.out.println("扣减失败,库存不足");
}
} finally {
stringRedisTemplate.delete(lockKey);
}
return "end";
}
}
解决办法
@RestController
public class indexController {
@Autowired
private Redisson redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct_stock")
public String deductStock(){
String lockKey = "lockKey";//简单的不完善的分布式状态锁
String s = UUID.randomUUID().toString();//为防止进程错误删除其他进程的锁,给进程的做提添加一个uuid标志
try {
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent("lockKey", "zhujian",10, TimeUnit.SECONDS);//jedis.setnx(key,value)
if(!result){
return "error_code";//前端根据错误状态码,做出友好提示
}
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if(stock > 0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock",realStock + " ");
System.out.println("扣减成功,剩余库存:"+realStock);
}else {
System.out.println("扣减失败,库存不足");
}
} finally {
stringRedisTemplate.delete(lockKey);
}
return "end";
}
}
上面虽然可以防止进程错误删除其他进程的锁,但是当锁被释放掉时,在高并发场景下难免会导致uuid的重复,导致多个进程仍会错删其他进程的锁,仍会引起超卖问题。
解决办法:在后台加个分线程,对分分线程加个定时任务,判断主线程是否还存在当时添加的锁,如果存在就给他延长当初加锁的时间。(代码实现较难),目前市场有很多开源框架,例如redisson,具体逻辑如下图:
redisson实现上述逻辑(内部有看门狗机制),如下代码
@RestController
public class indexController {
@Autowired
private Redisson redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct_stock")
public String deductStock(){
String lockKey = "lockKey";//简单的不完善的分布式状态锁
RLock reLock = redisson.getLock(lockKey);
try {
reLock.lock();//相当于setIfAbsent("lockKey", "zhujian",10, TimeUnit.SECONDS);
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if(stock > 0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock",realStock + " ");
System.out.println("扣减成功,剩余库存:"+realStock);
}else {
System.out.println("扣减失败,库存不足");
}
} finally {
reLock.unlock();
}
return "end";
}
}