缓存问题(一)添加redis缓存与扩展"/>
Redis的缓存问题(一)添加redis缓存与扩展
什么是缓存?
缓存的作用与成本
作用
成本
如何添加redis缓存?
缓存作用模型
需求分析
代码实现
ShopController
IShopService 业务层接口
ShopServiceImpl 业务层实现类
扩展功能实现
视频地址如下
代码详情
完整代码,需要自取(完整无套路)
什么是缓存?
缓存就是数据交换的缓冲区(称作Cache),是存贮数据的临时地方,一般读写性能较高。
缓存的作用与成本
作用
- 当请求进入Tomcat以后,以前是查询数据库,而数据库本身是存在磁盘中的,查询需要进行IO操作,耗时,会给数据库造成压力;但是当我们有了缓存,请求进入Tomcat以后,直接进入Redis,查到后将结果返回给前端,就减轻了后端的负载。
- 基于内存,所以读写快!
成本
- 同一份数据在Redis与数据库中是要保证一致的!
- 代码会更加复杂。
- 为了高可用、防止雪崩,可能需要搭建集群,需要运维成本。
如何添加redis缓存?
缓存作用模型
先查询Redis,如果命中,直接将数据取出;反之未命中,则查询MySQL数据库,若有数据返回客户端,并且将其写入Redis缓存中。
需求分析
查询导航栏,点击后查看
我们需要的就是给API添加缓存
代码实现
ShopController
之前是直接访问数据库,如下:
@GetMapping("/{id}")
public Result queryShopById(@PathVariable("id") Long id) {return Result.ok(shopService.getById(id));
}
但是这里需要用到redis,步骤比较多,所以我们在Controller层需要调用Service层去实现该逻辑。
@GetMapping("/{id}")
public Result queryShopById(@PathVariable("id") Long id) {//return Result.ok(shopService.getById(id));return shopService.queryById(id);
}
IShopService 业务层接口
public interface IShopService extends IService<Shop> {Result queryById(Long id);
}
ShopServiceImpl 业务层实现类
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {// 1.从redis查询商铺缓存String key = CACHE_SHOP_KEY + id;String shopJson = stringRedisTemplate.opsForValue().get(key);// 2.判断是否存在if (StrUtil.isNotBlank(shopJson)) {// 3.存在,直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);System.out.println("Redis");return Result.ok(shop);}// 4.不存在,根据id查询数据库Shop shop = getById(id);// 5.不存在,返回错误if (shop == null) {return Result.fail("店铺不存在!");}// 6.存在,写入Redis// 把shop转换成为JSON形式写入RedisSystem.out.println("MySQL");stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop));return Result.ok(shop);}
}
如下所示: 这些数据一旦被访问就会被写入Redis中。
此处,redis的存储结构我们采用的是string的结构(当然使用Hash也是可以的)
我们可以看看打印控制台
显然第一次查询执行了SQL,第二次就是直接从Redis去数据了!
Preparing: SELECT id,name,type_id,images,area,address,x,y,avg_price,sold,comments,score,open_hours,create_time,update_time FROM tb_shop WHERE id=?
日志总览
2022-07-06 14:12:00.497 DEBUG 7464 --- [nio-8081-exec-4] c.h.m.VoucherMapper.queryVoucherOfShop : ==> Preparing: SELECT v.`id`, v.`shop_id`, v.`title`, v.`sub_title`, v.`rules`, v.`pay_value`, v.`actual_value`, v.`type`, sv.`stock` , sv.begin_time , sv.end_time FROM tb_voucher v LEFT JOIN tb_seckill_voucher sv ON v.id = sv.voucher_id WHERE v.shop_id = ? AND v.status = 1
2022-07-06 14:12:00.498 DEBUG 7464 --- [nio-8081-exec-4] c.h.m.VoucherMapper.queryVoucherOfShop : ==> Parameters: 1(Long)
2022-07-06 14:12:00.500 DEBUG 7464 --- [nio-8081-exec-4] c.h.m.VoucherMapper.queryVoucherOfShop : <== Total: 1
2022-07-06 14:12:00.500 DEBUG 7464 --- [nio-8081-exec-2] com.hmdp.mapper.ShopMapper.selectById : ==> Preparing: SELECT id,name,type_id,images,area,address,x,y,avg_price,sold,comments,score,open_hours,create_time,update_time FROM tb_shop WHERE id=?
2022-07-06 14:12:00.500 DEBUG 7464 --- [nio-8081-exec-2] com.hmdp.mapper.ShopMapper.selectById : ==> Parameters: 1(Long)
2022-07-06 14:12:00.503 DEBUG 7464 --- [nio-8081-exec-2] com.hmdp.mapper.ShopMapper.selectById : <== Total: 1
MySQL
2022-07-06 14:12:05.146 DEBUG 7464 --- [nio-8081-exec-3] c.h.m.VoucherMapper.queryVoucherOfShop : ==> Preparing: SELECT v.`id`, v.`shop_id`, v.`title`, v.`sub_title`, v.`rules`, v.`pay_value`, v.`actual_value`, v.`type`, sv.`stock` , sv.begin_time , sv.end_time FROM tb_voucher v LEFT JOIN tb_seckill_voucher sv ON v.id = sv.voucher_id WHERE v.shop_id = ? AND v.status = 1
2022-07-06 14:12:05.146 DEBUG 7464 --- [nio-8081-exec-3] c.h.m.VoucherMapper.queryVoucherOfShop : ==> Parameters: 1(Long)
2022-07-06 14:12:05.148 DEBUG 7464 --- [nio-8081-exec-3] c.h.m.VoucherMapper.queryVoucherOfShop : <== Total: 1
Redis
扩展功能实现
如下这些logo一般是长期存在的,每次登入我们都要重新加载,所以这里将其缓存在Redis中,来提高网页的效率问题。
在黑马程序员的视频中,P37的视频留了一道练习题,我们在这里将其实现以下:
视频地址如下
黑马程序员Redis入门到实战教程,全面透析redis底层原理+redis分布式锁+企业解决方案+redis实战_哔哩哔哩_bilibili=37&vd_source=470cf4bd5833d6feeb5c5aaefc5e3465
代码详情
ShopTypeController
调用Service层,在Service中去做具体的实现
@RestController
@RequestMapping("/shop-type")
public class ShopTypeController {@Resourceprivate IShopTypeService typeService;@GetMapping("list")public Result queryTypeList() {
// List<ShopType> typeList = typeService
// .query().orderByAsc("sort").list();
// return Result.ok(typeList);return typeService.getByIconList();}
}
ShopTypeServiceImpl
@Service
public class ShopTypeServiceImpl extends ServiceImpl<ShopTypeMapper, ShopType> implements IShopTypeService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result getByIconList() {// 1.在redis中间查询String key = CACHE_SHOP_TYPE_KEY;List<String> shopTypeList = new ArrayList<>();// range()中的 -1 表示最后一位// shopTypeList中存放的数据是[{...},{...},{...}...] 一个列表中有一个个json对象shopTypeList = stringRedisTemplate.opsForList().range(key,0,-1);// 2.判断是否缓存中了// 3.中了返回 (判断redis不空)if(!shopTypeList.isEmpty()) {List<ShopType> typeList = new ArrayList<>();for (String s : shopTypeList) {ShopType shopType = JSONUtil.toBean(s, ShopType.class);// shopType 是一个对象typeList.add(shopType);}return Result.ok(typeList);}// 4.redis未命中数据,从数据库中获取,根据ShopType对象的sort属性排序后存入typeListList<ShopType> typeList = query().orderByAsc("sort").list();// 5.数据库中如果不存在直接返回错误if(typeList.isEmpty()){return Result.fail("不存在分类");}for(ShopType shopType : typeList){String s = JSONUtil.toJsonStr(shopType);shopTypeList.add(s);}// 6.存在直接添加进缓存stringRedisTemplate.opsForList().rightPushAll(key, shopTypeList);return Result.ok(typeList);}
}
注意点:
这里有一些东西需要解释一下,stringRedisTemplate.opsForList().range(key,0,-1); 中的range的 -1 代表的是最后一个的意思,即这些logo的数量为10个所以range(key,0,10);是一个意思。
代码实现思路与上面的样例类似,只是需要注意的是返回的 typeList 是一个 List 列表。
完整代码,需要自取(完整无套路)
链接:
提取码:l2hh
--来自百度网盘超级会员V4的分享
更多推荐
Redis的缓存问题(一)添加redis缓存与扩展
发布评论