☃️什么是缓存?
缓存就像自行车,越野车的避震器
举个栗子: 越野车,山地自行车,都拥有"避震器",防止车体加速后因惯性,在酷似"U"字母的地形上飞跃,硬着陆导致的损害,像个弹簧一样;
同样,实际开发中,系统也需要"避震器", 防止过高的数据访问猛冲系统,导致其操作线程无法及时处理信息而瘫痪;
这在实际开发中对企业讲,对产品口碑,用户评价都是致命的; 所以企业非常重视缓存技术;
缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地.
❄️❄️为什么要使用缓存
一句话:因为 响应速度快, 用户体验用
缓存数据存储于代码中,而代码运行在内存中,内存的读写性能远高于磁盘,缓存可以大大降低用户访问并发量带来的 服务器读写压力
实际开发过程中,企业的数据量,少则几十万,多则几千万,这么大数据量,如果没有缓存来作为"避震器",系统是几乎撑不住的,所以企业会大量运用到缓存技术;
但是缓存也会增加代码复杂度和运营的成本:
- 数据一致性成本
- 代码维护成本
- 运维成本
❄️❄️如何使用缓存
实际开发中,会构筑多级缓存来使系统运行速度进一步提升,例如: 本地缓存与redis中的缓存结合使用
浏览器缓存:主要是存在于浏览器端的缓存
应用层缓存: 可以分为tomcat本地缓存,比如之前提到的map,或者是使用redis作为缓存
数据库缓存: 在数据库中有一片空间是 buffer pool,增改查数据都会先加载到mysql的缓存中
CPU缓存: 当代计算机最大的问题是 cpu性能提升了,但内存读写速度没有跟上,所以为了适应当下的情况,增加了cpu的L1,L2,L3级的缓存
☃️缓存实战
当我们查询热点信息时, 我们如果直接去数据库查, 高并发下那响应时间肯定慢, 所以我们需要增加缓存.
❄️❄️缓存模型和思路
标准的操作方式就是查询数据库之前先查询缓存,如果缓存数据存在,则直接从缓存中返回,如果缓存数据不存在,再查询数据库,然后将数据存入 Redis
。
❄️❄️演示代码
public Result get(Long id){ if( id < 0 ){ return Result.ok(null); } String key = "XXXX:" + id; // 先从redis缓存查 String value = redisTemplate.opsForValue().get(key); // 判断是否存在 if( StringUtils.isNotBlank(value) ){ // 存在直接返回 return Result.ok(JSON.parse(value, WorkTicket.class)); } // 不存在去数据库查 WorkTicket workTicket = workTicketService.getById(id); // 不存在 返回空 if( workTicket == null ){ return Result.ok(null); } // 写入缓存 redisTemplate.opsForValue().set(key, JSON.toJSONString(workTicket)); return Result.ok(workTicket); }
☃️缓存更新策略
缓存更新是Redis为了节约内存而设计出来的一个东西,主要是因为内存数据宝贵,当我们向Redis插入太多数据,此时就可能会导致缓存中的数据过多,所以Redis会对部分数据进行更新,或者把他叫为淘汰更合适。
内存淘汰: Redis自动进行,当Redis内存达到咱们设定的 max-memery
的时候,会自动触发淘汰机制,淘汰掉一些不重要的数据(可以自己设置策略方式)
超时剔除: 当我们给Redis设置了过期时间 ttl
之后,Redis 会将超时的数据进行删除
主动更新: 我们可以手动调用方法把缓存删掉,通常用于解决缓存和数据库不一致问题
业务场景:
低一致性需求:使用内存淘汰机制。
高一致性需求:主动更新,并以超时剔除作为兜底方案。
❄️❄️数据库缓存不一致解决方案
由于我们的 缓存的数据源来自于数据库, 而数据库的 数据是会发生变化的, 因此,如果当数据库中 数据发生变化,而缓存却没有同步, 此时就会有 一致性问题存在, 其后果是:
用户使用缓存中的过时数据,就会产生类似多线程数据安全问题,从而影响业务,产品口碑等;怎么解决呢?有如下几种方案
人工编码方式:缓存调用者在更新完数据库后再去更新缓存,也称之为双写方案
由系统本身完成: 数据库与缓存的问题交由系统本身去处理
调用者只操作缓存: 其他线程去异步处理数据库,实现最终一致