redis实战-缓存数据&解决缓存与数据库数据划一性

打印 上一主题 下一主题

主题 967|帖子 967|积分 2901

缓存的定义

缓存(Cache),就是数据互换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码。防止过高的数据访问猛冲体系,导致其操纵线程无法及时处理信息而瘫痪,这在实际开辟中对企业讲,对产品口碑,用户评价都是致命的;所以企业非常重视缓存技能,redis作为最常用的缓存中间件,也是面试的高频考点。
使用缓存的目的

缓存数据存储于代码中,而代码运行在内存中,内存的读写性能远高于磁盘,缓存可以大大降低用户访问并发量带来的服务器读写压力。实际开辟过程中,企业的数据量,少则几十万,多则几千万,这么大数据量,如果没有缓存,体系是几乎撑不住的,所以企业会大量运用到缓存技能。


怎样使用缓存

实际开辟中,会构筑多级缓存来使体系运行速率进一步提升,例如:本地缓存与redis中的缓存并发使用
   欣赏器缓存:主要是存在于欣赏器端的缓存
  应用层缓存:可以分为tomcat本地缓存,比如之前提到的map,或者是使用redis作为缓存
  数据库缓存:在数据库中有一片空间是 buffer pool,增改查数据都会先加载到mysql的缓存中
  CPU缓存:当代计算机最大的题目是 cpu性能提升了,但内存读写速率没有跟上,所以为了顺应当下的环境,增加了cpu的L1,L2,L3级的缓存
  缓存商店信息

 商店信息接口具有很高的并发量,查询数据不能每次都从数据库查询,要将商店数据缓存到redis中,来应对高并发
  1. @GetMapping("/{id}")
  2. public Result queryShopById(@PathVariable("id") Long id) {
  3.     //这里是直接查询数据库
  4.     return shopService.queryById(id);
  5. }
复制代码

缓存模子和思绪

标准的操纵方式就是查询数据库之前先查询缓存,如果缓存数据存在,则直接从缓存中返回,如果缓存数据不存在,再查询数据库,然后将数据存入redis。

 代码实现

留意此时缓存数据不设置过期时间,为了减轻数据库压力,缓存应该常驻在内存中,但也会带来一个那就是缓存数据与数据库数据不划一的题目,这就引出了缓冲更新策略这一题目
  1. @Override
  2.     public Result queryById(Long id) {
  3.         //根据业务代码组装key
  4.         String key = CACHE_SHOP_KEY + id;
  5.         //从redis中获取商铺信息
  6.         String shopJson = stringRedisTemplate.opsForValue().get(key);
  7.         if (StrUtil.isNotBlank(shopJson)) {
  8.             //将json转化为shop对象直接返回
  9.             Shop shop = JSONUtil.toBean(shopJson, Shop.class);
  10.             return Result.ok(shop);
  11.         }
  12.         Shop shop = getById(id);
  13.         if (shop == null) {
  14.             return Result.fail("店铺不存在");
  15.         }
  16.         //将数据库查询的数据写入缓存
  17.         stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop));
  18.         //返回
  19.         return Result.ok(shop);
复制代码
缓存更新策略

更新策略主要根据业务来选择,在本项目中采用的是自动更新+超时剔除的更新策略,超时剔除主要是作为保底的更新策略,保证缓存在没有触发自动更新的环境下,每隔一段时间就会清理缓存
   缓存更新是redis为了节约内存而计划出来的一个东西,主要是因为内存数据名贵,当我们向redis插入太多数据,此时就可能会导致缓存中的数据过多,所以redis会对部分数据举行更新,或者把他叫为淘汰更符合。
  内存淘汰:redis自动举行,当redis内存达到咱们设定的max-memery的时间,会自动触发淘汰机制,淘汰掉一些不重要的数据(可以本身设置策略方式)
  超时剔除:当我们给redis设置了过期时间ttl之后,redis会将超时的数据举行删除,方便咱们继续使用缓存
  自动更新:我们可以手动调用方法把缓存删掉,通常用于解决缓存和数据库不划一题目
  

 数据库缓存数据不划一解决方案

由于我们的缓存的数据源来自于数据库,而数据库的数据是会发生变化的,因此,如果当数据库中数据发生变化,而缓存却没有同步,此时就会有划一性题目存在,其后果是:
用户使用缓存中的过时数据,就会产生类似多线程数据安全题目,从而影响业务,产品口碑等;怎么解决呢?有如下几种方案
   Cache Aside Pattern 人工编码方式:缓存调用者在更新完数据库后再去更新缓存,也称之为双写方案
  Read/Write Through Pattern : 由体系本身完成,数据库与缓存的题目交由体系本身去处理
  Write Behind Caching Pattern :调用者只操纵缓存,其他线程去异步处理数据库,实现最终划一
  使用方案二增加了体系复杂度,倒霉于调用者排查有关题目,方案三会有一系列线程安全,造成数据库缓存不划一的环境,经过综合考虑选用人工编码的方式较为稳妥
人工编码步骤


  • 删除缓存:更新数据库时让缓存失效,查询时再更新缓存,(如果是更新数据库的同时,更新缓存,会有太多更新动作,无法保证性能)
  • 在单体体系中,将缓存与数据库操纵放在一个事务,保证更新数据库成功时,缓存也要添加成功,即保证两个操纵同时成功或失败
  • 先操纵数据库,再删除缓存,在多线程的环境下,操纵数据库的时间要比操纵redis缓存的时间多得多,出现数据库写完,缓存失效的可能性较小
实现商店和缓存与数据库双写划一





  • 根据id查询店铺时,如果缓存未命中,则查询数据库,将数据库结果写入缓存,并设置超时时间
  • 根据id修改店铺时,先修改数据库,再删除缓存
添加缓存时设置redis缓存时添加过期时间
  1. @Override
  2.     public Result queryById(Long id) {
  3.         //根据业务代码组装key
  4.         String key = CACHE_SHOP_KEY + id;
  5.         //从redis中获取商铺信息
  6.         String shopJson = stringRedisTemplate.opsForValue().get(key);
  7.         if (StrUtil.isNotBlank(shopJson)) {
  8.             //将json转化为shop对象直接返回
  9.             Shop shop = JSONUtil.toBean(shopJson, Shop.class);
  10.             return Result.ok(shop);
  11.         }
  12.         Shop shop = getById(id);
  13.         if (shop == null) {
  14.             return Result.fail("店铺不存在");
  15.         }
  16.         //将数据库查询的数据写入缓存,并设置过期时间
  17.         stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), 30L,TimeUnit.MINUTES);
  18.         //返回
  19.         return Result.ok(shop);
  20.     }
复制代码
我们确定了采用删除策略,来解决双写题目,当我们修改了数据之后,然后把缓存中的数据举行删除,查询时发现缓存中没有数据,则会从mysql中加载最新的数据,从而避免数据库和缓存不划一的题目,此方法必要加@Transactional注解来声明事务
  1. @Transactional
  2. @Override
  3. public Result update(Shop shop) {
  4.     Long id = shop.getId();
  5.     //判断id是否为空,因为可以绕过前端直接发送请求,此步必须判断
  6.     if (id == null) {
  7.         return Result.fail("店铺id不能为空");
  8.     }
  9.     //更新数据库
  10.     updateById(shop);
  11.     //删除缓存
  12.     stringRedisTemplate.delete(CACHE_SHOP_KEY + id);
  13.     return Result.ok();
  14. }
复制代码








免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

笑看天下无敌手

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表