【黑马点评】(二)缓存

(一) 什么是缓存

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

(二)添加商户缓存

在这里插入图片描述
在这里插入图片描述
控制层

 /**
  * 根据id查询商铺信息
  * @param id 商铺id
  * @return 商铺详情数据
  */
 @GetMapping("/{id}")
 public Result queryShopById(@PathVariable("id") Long id) {
   
   
     return shopService.queryById(id);
 }

service层

public interface IShopService extends IService<Shop> {
   
   

    Result queryById(Long id);
}


@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService{
   
   

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result queryById(Long id) {
   
   
        String key = CACHE_SHOP_KEY + id;
        // 1.从redis查询缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        // 2.判断是否存在
        if(StrUtil.isNotBlank(shopJson)){
   
   
            // 3.存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        // 4.不存在,查询数据库
        Shop shop = getById(id);
        if(shop == null){
   
   
            return Result.fail("店铺不存在:!");
        }
        // 5.存在, 存入redis
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop));

        return Result.ok(shop);
    }
}


测试效果:
在这里插入图片描述

(三)缓存练习题分析

在这里插入图片描述
自行实现即可,不难

(四)缓存更新策略

在这里插入图片描述

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

先删除缓存,在操作数据库的正常情况(缓存 数据库 一开始都是10)
在这里插入图片描述
产生不一致情况:
在这里插入图片描述

先操作数据库,在删除缓存的正常情况:
在这里插入图片描述
产生不一致情况:
在这里插入图片描述

方案二先操作数据库,在删除缓存比方案一概率更低,因为需要线程1恰好查询缓存的时候缓存是失效的,同时在准备写入缓存的很短的时间需要有线程二进来更新数据库,删除缓存,需要这两个条件同时成立。

在这里插入图片描述

(五)实现商铺缓存与数据库的双写一致

在这里插入图片描述

这里更新接口字段需要去掉updatetime和createtime,因为会报错,后续在找办法解决,子需要自定义配置时间就行,多个配置类。

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: raw timestamp (1642066339000) not allowed for java.time.LocalDateTime: need additional information such as an offset or time-zone (see class Javadocs); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: raw timestamp (1642066339000) not allowed for java.time.LocalDateTime: need additional information such as an offset or time-zone (see class Javadocs)
at [Source: (PushbackInputStream); line: 7, column: 19] (through reference chain: com.hmdp.entity.Shop[“updateTime”])

在这里插入图片描述

当执行更新店铺时,会更新数据库,在删除缓存

在这里插入图片描述

当再次查询时数据库时,会自动更新缓存
在这里插入图片描述

修改代码如下,添加缓存时候设置过期时间,然后在更新数据库时删除缓存。

    @Override
    public Result queryById(Long id) {
   
   
        String key = CACHE_SHOP_KEY + id;
        // 1.从redis查询缓存
        String shopJson = stringRedisTemplate.opsForValue().get(key);
        // 2.判断是否存在
        if(StrUtil.isNotBlank(shopJson)){
   
   
            // 3.存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        // 4.不存在,查询数据库
        Shop shop = getById(id);
        if(shop == null){
   
   
            return Result.fail("店铺不存在:!");
        }
        // 5.存在, 存入redis
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);

        return Result.ok(shop);
    }

    @Override
    @Transactional
    public Result update(Shop shop) {
   
   
        Long id = shop.getId();
        if(id == null){
   
   
      
### 黑马点评使用的缓存类型或实现方式 黑马点评项目中主要采用了 **Redis** 作为缓存工具,其具体实现方式可以分为两种常见的模式:基于 `String` 类型的缓存和基于 `List` 类型的缓存。 #### 1. 基于 Redis 的 String 类型缓存 在这种实现方式下,缓存的数据通常是以键值对的形式存储在 Redis 中。以下是其实现的关键点: - 数据存储时会将对象序列化为 JSON 字符串并保存到 Redis 中[^2]。 - 查询缓存时,如果命中,则直接反序列化 JSON 字符串为对应的 Java 对象列表[^2]。 - 如果未命中缓存,则从数据库中获取数据,并将其重新写回 Redis 缓存[^2]。 代码示例如下: ```java @Override public Result queryTypeList() { // 1. 从 redis 中查询店铺类型的缓存 String shopTypeJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY); // 2. 判断是否命中 if (StrUtil.isNotBlank(shopTypeJson)) { // 命中则直接返回 -- 将 json 转为字符串 List<ShopType> shopTypes = JSONUtil.toList(shopTypeJson, ShopType.class); return Result.ok(shopTypes); } // 3. 未命中 —— 查询数据库 List<ShopType> shopTypes = query().orderByAsc("sort").list(); // 4. 判断店铺类型是否存在 if (CollectionUtils.isEmpty(shopTypes)) { return Result.fail("店铺类型不存在"); } // 5. 存在则将店铺类型信息写入 redis 中 stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY, JSONUtil.toJsonStr(shopTypes)); return Result.ok(shopTypes); } ``` 这种实现方式的优点在于简单高效,适合处理单条记录或者小型集合的缓存场景。 --- #### 2. 基于 Redis 的 List 类型缓存 对于更复杂的数据结构,比如需要频繁增删改查的操作,可能会采用 Redis 的 `List` 类型来实现缓存。这种方式的主要特点如下: - 数据存储时,会将每个对象转化为字符串形式并通过循环逐一插入到 Redis 的 `List` 结构中[^4]。 - 查询缓存时,通过读取整个 `List` 并将其转换为 Java 集合的方式完成操作[^4]。 - 判断缓存是否存在可以通过检查 `List` 的长度是否大于零来进行[^4]。 相比 `String` 类型的缓存,`List` 类型更适合用于动态变化较大的数据集,但由于每次都需要逐一遍历,性能可能稍逊一些。 --- #### 缓存更新的最佳实践 无论使用哪种缓存类型,在实际开发过程中都应遵循以下最佳实践以确保数据一致性: - 主动更新策略:当数据库中的数据发生变化时,同步清除相应的缓存项[^1]。 - 示例代码展示如何在更新商铺信息的同时维护缓存的一致性: ```java public Result update(Shop shop) { Long id = shop.getId(); if (id == null){ return Result.fail("店铺id不能为空!"); } // 1. 更新数据库 updateById(shop); // 2. 删除缓存 stringRedisTemplate.delete(RedisConstants.CACHE_SHOP_KEY + id); return Result.ok(); } ``` 这种方法能够有效减少因缓存过期而导致的数据不一致问题。 --- ### 总结 综上所述,黑马点评项目中常用的缓存类型主要包括基于 Redis 的 `String` 和 `List` 两种实现方式。前者适用于静态或较少变动的数据,而后者则更加灵活但代价较高。同时,为了保障系统的稳定性和效率,还需要配合合理的缓存更新机制。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值