**缓存穿透 :**缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。

**缓存空对象(常用方案):**当没有命中redis时,并且查询数据库也为空,那我们不返回404,而是返回一个空对象存储在redis中并设置一个TTL,防止频繁访问数据库。
主动措施:
1.增强id的复杂度,避免被猜测id规律
2.做好数据的基础格式校验
3.加强用户权限校验
4.做好热点参数的限流
下面我们就根据这个方案修改原先的缓存方案

我们在原来的基础上,当商铺不存在时将空值写入redis,并且在缓存校验时,判断是否为空值,如果是就直接返回错误信息,不是就返回商户信息
java
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 根据id查询商铺信息
*/
@Override
public Result queryById(Long id) {
//从redis查缓存
String key = CACHE_SHOP_KEY+ id;
String shopJson = stringRedisTemplate.opsForValue().get(key);
//判断是否存在
if(StrUtil.isNotBlank(shopJson)){
//存在,直接返回
Shop shop = JSONUtil.toBean(shopJson, Shop.class);
return Result.ok(shop);
}
//判断命中是否为空值
if(shopJson!=null){
return Result.fail("店铺不存在");
}
//不存在,根据id查询数据库
Shop shop = getById(id);
if(shop == null){
//不存在,将控制写入redis
stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);
return Result.fail("商铺不存在");
}
//存在,写入redis
stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);
//返回商铺信息
return Result.ok(shop);
}
/**
* 更新商铺信息
* @param shop
*/
@Transactional
@Override
public Result update(Shop shop) {
Long id = shop.getId();
if(id==null){
return Result.fail("商铺id不能为空");
}
String key = CACHE_SHOP_KEY + id;
//更新数据库
updateById(shop);
//删除缓存
stringRedisTemplate.delete(key);
return Result.ok(shop);
}
}