国家建设部官方网站赵宏彦,济南网站建设工作室,建筑方案设计收费标准,哈尔滨小程序建设缓存的定义
缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码。防止过高的数据访问猛冲系统,导致其操作线程无法及时处理信息而瘫痪#xff0c;这在实际开发中对企业讲,对产品口碑,用户评价都是致命的;所以企业非常重视缓存…缓存的定义
缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码。防止过高的数据访问猛冲系统,导致其操作线程无法及时处理信息而瘫痪这在实际开发中对企业讲,对产品口碑,用户评价都是致命的;所以企业非常重视缓存技术redis作为最常用的缓存中间件也是面试的高频考点。
使用缓存的目的
缓存数据存储于代码中,而代码运行在内存中,内存的读写性能远高于磁盘,缓存可以大大降低用户访问并发量带来的服务器读写压力。实际开发过程中,企业的数据量,少则几十万,多则几千万,这么大数据量,如果没有缓存,系统是几乎撑不住的,所以企业会大量运用到缓存技术。 如何使用缓存
实际开发中,会构筑多级缓存来使系统运行速度进一步提升,例如:本地缓存与redis中的缓存并发使用 浏览器缓存主要是存在于浏览器端的缓存 应用层缓存可以分为tomcat本地缓存比如之前提到的map或者是使用redis作为缓存 数据库缓存在数据库中有一片空间是 buffer pool增改查数据都会先加载到mysql的缓存中 CPU缓存当代计算机最大的问题是 cpu性能提升了但内存读写速度没有跟上所以为了适应当下的情况增加了cpu的L1L2L3级的缓存 缓存商铺信息 商铺信息接口具有很高的并发量查询数据不能每次都从数据库查询要将商铺数据缓存到redis中来应对高并发
GetMapping(/{id})
public Result queryShopById(PathVariable(id) Long id) {//这里是直接查询数据库return shopService.queryById(id);
} 缓存模型和思路
标准的操作方式就是查询数据库之前先查询缓存如果缓存数据存在则直接从缓存中返回如果缓存数据不存在再查询数据库然后将数据存入redis。 代码实现
注意此时缓存数据不设置过期时间为了减轻数据库压力缓存应该常驻在内存中但也会带来一个那就是缓存数据与数据库数据不一致的问题,这就引出了缓冲更新策略这一问题
Overridepublic Result queryById(Long id) {//根据业务代码组装keyString key CACHE_SHOP_KEY id;//从redis中获取商铺信息String shopJson stringRedisTemplate.opsForValue().get(key);if (StrUtil.isNotBlank(shopJson)) {//将json转化为shop对象直接返回Shop shop JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}Shop shop getById(id);if (shop null) {return Result.fail(店铺不存在);}//将数据库查询的数据写入缓存stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop));//返回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缓存时添加过期时间
Overridepublic Result queryById(Long id) {//根据业务代码组装keyString key CACHE_SHOP_KEY id;//从redis中获取商铺信息String shopJson stringRedisTemplate.opsForValue().get(key);if (StrUtil.isNotBlank(shopJson)) {//将json转化为shop对象直接返回Shop shop JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}Shop shop getById(id);if (shop null) {return Result.fail(店铺不存在);}//将数据库查询的数据写入缓存,并设置过期时间stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), 30L,TimeUnit.MINUTES);//返回return Result.ok(shop);}
我们确定了采用删除策略来解决双写问题当我们修改了数据之后然后把缓存中的数据进行删除查询时发现缓存中没有数据则会从mysql中加载最新的数据从而避免数据库和缓存不一致的问题此方法需要加Transactional注解来声明事务
Transactional
Override
public Result update(Shop shop) {Long id shop.getId();//判断id是否为空因为可以绕过前端直接发送请求此步必须判断if (id null) {return Result.fail(店铺id不能为空);}//更新数据库updateById(shop);//删除缓存stringRedisTemplate.delete(CACHE_SHOP_KEY id);return Result.ok();
}