
public Shop huancunjichuan(Long id) {//基于互斥锁实现Redis的缓存击穿
//从redis查询
String key = CACHE_SHOP_KEY + id;
String shop = stringRedisTemplate.opsForValue().get(key);
//判断是否存在
if (StrUtil.isNotBlank(shop)) {
//命中返回
Shop shop1 = JSONUtil.toBean(shop, Shop.class);
return shop1;
}
if (shop != null) {
return null;
}
String lockkey = null;
Shop byId = null;
try {
lockkey = LOCK_SHOP_KEY + id;
Boolean istguo = tryLock(lockkey);
if (!istguo) {
Thread.sleep(50);
return huancunjichuan(id);
}
String shop2 = stringRedisTemplate.opsForValue().get(key);
if (shop2 != null) {
return JSONUtil.toBean(shop2, Shop.class);
}
//未命中查数据库
byId = getById(id);
if (byId == null) {
//不存在返回404
stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);//把空值存入redis
return null;
}
//存在写入redis
stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(byId), CACHE_SHOP_TTL, TimeUnit.MINUTES);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
//释放锁
unLock(lockkey);
}
return byId;
}

缓存穿透

雪崩

击穿


全局ID

订单秒杀
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
@Resource
ISeckillVoucherService ISeckillVoucherService;
@Resource
RedisIDWorker RedisIDWorker;
@Override
@Transactional
public Result seckillVoucher(Long voucherId) {
SeckillVoucher Voucher = ISeckillVoucherService.getById(voucherId);
if (Voucher.getBeginTime().isAfter(LocalDateTime.now())) {
return Result.fail("别急还没开始呢!");
}
if (Voucher.getEndTime().isBefore(LocalDateTime.now())) {
return Result.fail("时间已经过了!");
}
if ((Voucher.getStock() < 1)) {
return Result.fail("库存不足!");
}
Long id = UserHolder.getUser().getId();
synchronized (id.toString().intern()) {
IVoucherService proxy = (IVoucherService) AopContext.currentProxy();//获取spring代理的类来保证事务的可用
return proxy.createVoucherorder(voucherId);
/**
*要在pom.xml里引入
* <dependency>
* <groupId>org.aspectj</groupId>
* <artifactId>aspectjweaver</artifactId>
*</dependency>
*否则获取不到
*/
}
}
@Transactional
public Result createVoucherorder(Long voucherId) {
Long id = UserHolder.getUser().getId();
Integer count = query().eq("user_id", id).eq("voucher_id", voucherId).count();
if (count > 0) {
return Result.fail("用户已经卖过一次了");
}
boolean voucherjieguo = ISeckillVoucherService.update().setSql("stock = stock - 1")
.eq("voucher_id", voucherId)
.gt("stock", 0)
.update();
if (!voucherjieguo) {
return Result.fail("库存不足!");
}
VoucherOrder voucherOrder = new VoucherOrder();
Long orderid = RedisIDWorker.nextID("Order");
voucherOrder.setId(orderid);
//从线程储存获取用户信息
voucherOrder.setUserId(id);
voucherOrder.setVoucherId(voucherId);
save(voucherOrder);
return Result.ok(orderid);
}
}
加载lua脚本
lua脚本
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by song.
--- DateTime: 2022/12/12 15:03
---
--订单id
local voucherId =ARGV[1]
local userId =ARGV[2]
--用户id
--库存key
local stockKey = "seckill:stock:"..voucherId
--订单key
local orderKey ="Iseckill:order:"..voucherId
--判断储存是否充足
if(tonumber(redis.call('get',stockKey))<=0) then
--库存不足返回1
return 1
end
--判断是否存在重复下单
if (redis.call("sismember",orderKey,userId)==1) then
return 2--存在重复下单返回2
end
--减库存
redis.call('incrby',stockKey,-1)
redis.call('sadd',orderKey,userId)
return 0
