缓存练习题(用户查询操作)
public List<ShopType> queryAllType() throws JsonProcessingException {//从缓存中查数据String shopTypeJson = stringRedisTemplate.opsForValue().get("cache:shopType");//如果缓存命中,if (StrUtil.isNotBlank(shopTypeJson)) {return new ObjectMapper().readValue(shopTypeJson, new TypeReference<List<ShopType>>() {});}//如果缓存不命中,查数据库List<ShopType> shopTypeList = shopTypeMapper.selectList(null).stream().sorted(Comparator.comparingInt(ShopType::getSort)).collect(Collectors.toList());if (shopTypeList == null) {return null;} else {stringRedisTemplate.opsForValue().set("cache:shopType", new ObjectMapper().writeValueAsString(shopTypeList));}//如果过数据库不命中,则返回错误//如果过数据库命中,则更新缓存return null;}
缓存更新策略(更新商品操作)
缓存操作很快,操作数据库很慢
方案1:如果先删除缓存,再操作数据库,线程1 ,清空缓存,线程2 查询缓存未命中,写入数据库数据是10,此时线程1,数据库更新
方案2:缓存因为某种原因失效了,线程1缓存未命中查出数据库10,另外一个线程2更新数据库,删除缓存,写缓存时数据为20,但是这种情况基本不存在
结论:由于缓存速度很快,所以第二种情况发生概率很低,采用先操作数据库,再删除缓存
@Override@Transactionalpublic Result update(Shop shop) {if (shop.getId() == null) {return Result.fail("500");}//1、更新数据库updateById(shop);//2、删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY + shop.getId());return Result.ok();}
缓存穿透
缓存空对象
空的值缓存到Redis
优点:实现简单,维护方便
缺点:额外的内存消耗;造成短期的不一致(如果此时数据库真的增加了,在TTL过期以后才能查询到真数据)
设置TTL,比如5分钟,方式恶意用户访问
布隆过滤
优点:内存占用较少,没有多余key
缺点:存在误判可能
缓存雪崩
缓存击穿
互斥锁就是对于未命中数据,第一个线程获取锁并查询数据库更新缓存数据,其他线程抢不到锁就自旋等待,直到第一个线程更新缓存数据完成。
/*** 尝试获取锁** @param key* @return*/
private boolean tryLock(String key) {Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);
}/*** 释放锁** @param key*/
private void unlock(String key) {stringRedisTemplate.delete(key);
}
互斥锁就是对于命中数据,但是字段中过期的数据的处理,逻辑过期逻辑过期不是真正的过期,对于对应的Key我们并不需要去设置TTL,而是通过业务逻辑来达到一个类似于“过期”的效果。其本质还是限制落到数据库的请求数量!但前提是牺牲一致性保证可用性,还是上一个业务的接口,通过使用逻辑过期来解决缓存击穿:
这样一来,缓存基本是会被命中的,因为我没有给缓存设置任何过期时间,并且对于Key的set都是事先选择好的,如果出现未命中的情况基本可以判断他不在选择之内,这样我就可以直接返回错误信息。那么对于命中的情况,就需要先判断逻辑时间是否过期,根据结果再来决定是否进行缓存重建。而这里的逻辑时间就是减少大量请求落到数据库的一个“关口”
看完上面这一段,相信大家还很迷惑。既然没有设置过期时间,那你为什么还要判断逻辑过期时间,怎么还存在过不过期的问题?
其实,这里所谓的逻辑过期时间只是一个类的属性字段,根本没有上升到Redis,上升到缓存的层面,是用来辅助判断查询对象的,也就是说,所谓的过期时间与缓存数据是剥离开的,所以根本不存在缓存过期的问题,自然数据库也不会有压力。