文章目录
一、缓存是什么?
缓存就是数据交换的缓冲区(cache),是存储数据的临时地方,一般读写性能较高。
缓存的作用
- 降低后端的负载
- 提高读写效率
- 降低相应时间
缓存的作用模型
缓存的成本
- 维护数据一致性需要一定的成本(缓存的数据来自于数据库,但数据库的数据发生改变时,缓存中的数据也需要做出即时的改变,该过程的维护就是数据一致性需要的成本。)
- 代码维护成本
- 运维的成本
二、Redis缓存的更新策略
内存淘汰 | 超时删除 | 主动更新 | |
---|---|---|---|
说明 | 不用自己维护,利用Redis的内存淘汰机制,当内存不足的时候自动淘汰部分数据。下次查询时更新缓存。 | 给缓存数据添加TTL时间,到期后自动删除缓存(如大量缓存同时失效,可能会造成缓存雪崩问题)。下次查询时更新缓存 | 编写业务逻辑,在修改数据库的同时,更新缓存 |
一致性 | 差 | 一般 | 好 |
维护成本 | 无 | 低 | 高 |
分析-自动更新策略的实现方案
- Cache Aside Pattern:由缓存的调用者,在更新数据库的同时更新缓存
- Read/Write Through Pattern:缓存与数据库整合为一个服务,由服务器统一维护一致性。调用者调用改服务,无需关心缓存一致性问题。
- Writer Behind Caching Pattern:调用者只操作缓存,由其他线程异步将缓存数据持久化到数据库,保证最终的一致性
操作缓存和数据库时需要考虑的三个问题:
缓存更新策略的选择
- 低一致性需求:使用不错淘汰机制。如商品的分类的查询缓存等
- 高一致性需求:如商品的价格,销量的查询缓存等,随时间变化较快的属性
三.缓存穿透
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求就都会直接打到数据库。
常见的解决方案有如下两种
-
缓存空对象
优点:实现简单,方便维护
缺点:存在额外的内存消耗,甚至造成短期的数据不一致(例如某一时间点线程a查询数据K,在缓存和数据库中查询均未命中,这时Redis就会为数据K缓存对应的null值,过了一段时间数据,线程b先数据库插入了数据K且不为null,而此时缓存中数据K对应的值仍然为null,这就造成了数据不一致)
-
布隆过滤
优点:内存占用少,没有多余的key
缺点:实现复杂,存在误判的可能
缓存击穿的解决方案的各种解决方案
- 缓存null
- 布隆过滤
- 增强ID的复杂度,避免他人猜测ID规律,恶意攻击数据库
- 做好数据的基础格式校验
- 加强用户权限校验
- 做好热点参数的限量
四、缓存雪崩
缓存雪崩是指同一时间段大量的缓存key同时失效或者Redis服务器宕机,导致大量请求到达数据库,带来巨大的压力。
解决方案
- 给不同的Key的TTL添加随机值,避免大量Key同时过期失效
- 利用Redis集群提高服务器的可以性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
五、缓存击穿
缓存击穿问题也叫热点Key问题,是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问瞬间给数据库带来巨大的冲击。
缓存基础的两种解决方案
- 互斥锁
- 逻辑过期:给数据添加一个 expire属性值,例如:
KEY | VALUE |
---|---|
user:1 | {name:“Jay”,age":32,expire:123556} |
当expire设置的时间值失效,就会触发如下机制,避免缓存击穿问题
优缺点分析
互斥锁 | 逻辑过期 | |
---|---|---|
优点 | 没有额外的内存消耗 保证一致性 实现简单 | 线程无需等待 性能较好 |
缺点 | 线程需要等待,性能受到影响 存在死锁风险 | 不能保证数据的一致性 有额外的内存消耗 实现较为复杂 |