缓存

缓存

缓存的本质

  1. 缓存针对的是“资源”
  2. 缓存数据必须是“重复”获取的:空间换时间
  3. 缓存为了解决“开销”问题:包括时间、CPU、网络、I/O开销
  4. 缓存的存取不一定“更快”:降低延迟(latency)、增大吞吐量(throughput)

缓存应用模式

  1. Cache-Aside

img

数据更新策略:应用先更新数据库;应用再令缓存失效。

一、必须先更新数据库,而不是先令缓存失效,即这个顺序不能倒过来。原因在于,如果先令缓存失效,那么在数据库更新成功前,如果有另外一个请求访问了缓存,发现缓存数据库已经失效,于是就会按照数据获取策略,从数据库中使用这个已经陈旧的数值去更新缓存中的数据,这就导致这个过期的数据会长期存在于缓存中,最终导致数据不一致的严重问题。

**二、数据库更新以后,需要令缓存失效,而不是更新缓存为数据库的最新值。**为什么呢?你想一下,如果两个几乎同时发出的请求分别要更新数据库中的值为 A 和 B,如果结果是 B 的更新晚于 A,那么数据库中的最终值是 B。但是,如果在数据库更新后去更新缓存,而不是令缓存失效,那么缓存中的数据就有可能是 A,而不是 B。因为数据库虽然是“更新为 A”在“更新为 B”之前发生,但如果不做特殊的跨存储系统的事务控制,缓存的更新顺序就未必会遵从“A 先于 B”这个规则,这就会导致这个缓存中的数据会是一个长期错误的值 A。

  1. Read-Through

img

  1. Write-Through

应用要求缓存更新数据,如果缓存中存在,先更新数据,然后缓存再去更新数据库中的数据,缓存告知应用数据更新完成。

关键点是,缓存系统需要自己内部保证并发场景下,缓存更新的顺序要和数据库更新的顺序一致。

  1. Write-Back

更新操作发生的时候,数据写入缓存后立即返回,数据库的更新异步完成。

好处:请求吞吐量最大,操作迅速,数据库更新可以批量进行

坏处:异步更新一定会导致一致性问题,且存在数据丢失的风险

缓存带来的问题
1. 缓存穿透

在某些情况下,大量对同一个数据的访问,经过了缓存屏障,但是缓存却未能起到应有的保护作用。

例1,对某个key的查询,数据库里没有这个数据,那么缓存里也不会有,每次请求都会查数据库,缓存起不到应有的作用。

解决方法:缓存中,存储这个key的空结果;或者使用布隆过滤器,在数据库查询前,预先过滤掉不存在的结果

例2,一般的缓存策略下, 往往需要先发生一次缓存命中失败,然后从实际存储(如数据库)中得到结果,再存入缓存中。但是,如果这个数据库查询较慢,大量统一数据的请求几乎同时到来,就会全部穿透韩村,一并落到数据库上,而那个时候缓存回填甚至都没有发生,数据库就直接挂了,虽然缓存没有任何问题。

解决方法

  1. 流量控制,限制对同一数据的访问,必须等到前一个完成后,下一个才能进行。通用性好,但等待机制较为复杂,且可能影响客户体验。
  2. 缓存预热,在大批请求到来前,先主动将缓存填充好。操作简单高效,但需要提前知道哪些数据可能引发缓存穿透。
2. 缓存雪崩

原本起屏障作用的缓存,如果在一定时间段内,对于大量的请求访问失败,即失去了屏障作用,造成它后方的系统压力过大,引起系统过载、宕机等问题。

解决方法:流量控制、缓存预热

3. 缓存容量失控

最好是基于缓存容量本身来直接控制,但是考虑到某些编程语言的自身限制,比如 Java,从内存消耗的角度来实现不方便,那么就可以通过基于队列的长度来替代实现。

4. LRU的缺陷

短时间的并发访问冷门数据会破坏最近访问数据的真实性,导致穿透缓存。

解决方法:LRU-K算法,即主缓存队列排的是“第K次访问的元素”,低于K次访问量的存在低级队列,高于K次的放入主队列.

集成方式
  1. 编程方式:常以Cache-Aside模式应用
  2. 方法注解:对方法调用保持透明,不需要单独写缓存代码分散业务逻辑
  3. 配置文件注入:比如在Mybatis的mapper标签中指定cache标签,应用方式:Read/Write-Through模式
  4. web容器的Filter
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值