深入学习Redis笔记 | 青训营

98 阅读5分钟

先来说一下解决redis和mysql缓存一致性的方案

Cache Aside 读写策略

image.png Cache Aside 读写策略也叫缓存旁路读写策略,从上图可以看出,针对读写的策略分别是:

读流程: 首先从缓存中读取,如果有结果则直接返回结束 如果缓存中没有,则读 DB 并将结果回种缓存,然后返回结束 写流程: 首先将将结果写入 DB 然后删除缓存中对应的值 实际生产环境中,此种策略的使用也相对比较广泛,可以作为一种参考。这里需要注意一点的是,针对写流程,不能先删除缓存,在更新 DB,因为缓存删除后,此时 DB 还没有更新完时,来了一个 get 请求,那么缓存就有可能会被种入一个已失效的结果。

Cache Aside 读写策略优缺点对比

优点: 从流程图看,此种策略实现比较简单。 对于缓存和 DB 的一致性有了一定的保证,其可以解决第一种缓存方案遇到的问题。 缺点: 很显然,每次更新数据,都会先更新 DB 紧接着就删除缓存,如果读写操作都比较频繁的情况下,势必使得缓存的命中率有所折扣,也就意味着缓存的 miss 率升高,从而导致在一定程度上削弱了缓存的作用。 针对此种方案的缺点,其实也有一些比较折中的方案可以考虑。比如在更新 DB 完成后,同样更新缓存,但是在更新缓存的时候增加分布式锁避免;在比如如果业务场景并不是要求强一致性的话,可以将数据写入缓存并增加一个过期时间,这样即使数据不一致也只是一段时间。

对于增加过期时间的这种方式,存在极热 key 的场景并不是适用的,因为一旦 key 过期后,在一瞬间大量请求越过缓存,直冲 DB,也就造成了缓存穿透的问题,所以 Cache Aside 方案看起来很不错,但是也不是万能的。

Write/Read Through 策略 Write/Read Through 的缓存策略不同于前两种,该策略需要引入第三方缓存同步插件。其读写流程如下:

读流程: 首先读缓存,如果缓存命中,则直接返回结果 如果缓存未命中,则依赖第三方组件从 DB 中加载数据到缓存中,然后将获取的结果返回。 写流程: 要更新的数据是否在缓存中存在,若存在则直接将数据写入缓存,之后缓存数据由第三方缓存组件将其更新到 DB 若缓存中不存在,则直接将结果写入 DB,这种称之为写穿透 Write/Read Through 策略优缺点对比

优点: 写缓存 miss 的情况除外,剩余所有操作都只与缓存进行交互,很大程度上避免了缓存与 DB 一直性的情况 缺点: 从上面流程中不难看出,写缓存 miss 时直接与 DB 交互,会造成请求耗时增加 此种缓存策略引入了第三方缓存组件进行辅助,从而增加了系统的复杂度和系统的维护难度 由于需要引入第三方组件,而目前很多缓存如 Redis 原生并不兼容第三方组件,所以很难引入 总和上面这些情况对比,目前采用此种缓存读写策略的场景很少,作者到目前还没有见过使用这种缓存策略的场景。

再来说一下缓存三大热点问题

1.缓存穿透 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这些请求都会打到数据库

解决方案:①缓存空对象

优点: 实现简单,维护方便

缺点: 额外的内存消耗(即缓存过多的空对象,可以通过设置过期时间TTL缓解)

解决方案:②布隆过滤(BloomFilter )(推荐)

布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在于海量数据中。

2.缓存击穿

缓存击穿问题也叫热点Key问题,如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿的问题

解决方案:添加互斥锁

保证缓存与数据库的一致性,但是如果缓存重建时间过长,性能会有极大影响,甚至有死锁的风险,牺牲了可用性

3.缓存雪崩

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

针对大量数据同时过期的情况:

我们可以在对缓存数据设置过期时间时,给这些数据的过期时间加上一个随机数