Redis 缓存
什么是缓存
缓存 (Cache)是一种临时存储机制,用于存放经常访问的数据或计算结果,以便下次需要时可以更快速地获取,一般读写性能较高 。
缓存一般存储在内存中,它可以在计算机、网络设备或分布式系统中使用。
缓存的作用:
- 提高访问速度,降低延迟:缓存通常存储频繁访问的数据,相较于数据库、磁盘等存储介质,内存的读写速度更快,因此可以大幅提高数据访问的响应速度。
- 减少数据库负载:将常用的数据存储在缓存中,能够减少频繁的数据库访问,减轻数据库的负载,特别是在高并发情况下,这对于提升系统性能至关重要。
总而言之,缓存的核心作用是 提升性能,通过快速存取频繁使用的数据,减少对底层存储(如数据库、磁盘)的访问,提高响应速度,并减轻数据库等后端系统的压力。
添加redis缓存
访问数据的过程
- 查询缓存 :当用户请求某个数据时,应用程序首先会检查 Redis 缓存中是否已有该数据。
- 如果 缓存命中,即 Redis 中有该数据,直接从缓存中获取并返回给用户,避免了数据库查询。
- 如果 缓存未命中,即 Redis 中没有该数据,程序会继续去查询数据库。
- 查询数据库:如果缓存中没有该数据,应用程序会访问数据库获取数据。获取到数据后,通常会将该数据存入 Redis 缓存中,以便下次请求时直接从缓存获取。没有获取到数据就返回空
- 更新缓存 :在数据库查询后,将获取的数据存入 Redis,并设置一个 过期时间。下次请求时,数据就可以直接从缓存中返回,无需再访问数据库。
更新缓存
更新缓存 是缓存机制中非常重要的一环,主要是保证数据的一致性和及时性
一致性:由于同时将数据保存在缓存和数据库中,如果对数据库数据进行了修改,缓存不知道不去更新,那么用户查询的时候查到的就是旧数据。
缓存更新策略 有以下三种
内存淘汰、超时剔除、主动更新
--- | 内存淘汰 | 超时剔除 | 主动更新 |
---|---|---|---|
说明 | 不用自己维护,利用 redis 的内存淘汰机制,当内存不足时自动淘汰部分数据,下次查询时更新缓存 | 给缓存数据添加TTL时间,到期后自动删除缓存,下次查询时更新缓存 | 编写业务逻辑,在修改数据库的同时,更新缓存 |
一致性 | 差 | 一般 | 好 |
维护成本 | 无 | 低 | 高 |
缓存穿透
缓存穿透 指的是 客户端请求的数据在缓存和数据库中都不存在,这样请求就会直接绕过缓存,直接访问数据库。如果有大量这类数据,数据库就可能被被搞垮了
解决方案:
-
缓存空值
如果数据在缓存和数据库中都不存在,在缓存中写入一个空值 ,这样再查询此数据在redis 就可以命中,尽管命中的是null
优点: 实现简单,维护方便
缺点:-
额外的内存消耗(将大量不存在的数据写入缓存,redis 中缓存了大量垃圾,会有额外内存消耗),但是可以通过添加 TTL(有效期)进行一个优化,这样到期后就会清除垃圾数据
-
可能导致短期的不一致
用户请求了一个不存在 id,缓存为 null,就在此时,给这个 id 插入了一条数据,这时查询为 null,但实际上是存在的,只有当 TTL 到期后,才可以查询到最新的数据
-
-
布隆过滤
布隆过滤器 通过多个哈希函数,将请求的参数(比如商品 ID)映射到一个位数组中。如果查询的元素(如商品 ID)在布隆过滤器中不存在,那么说明该元素一定不在数据库中,可以直接返回错误,不去访问数据库
缺点:有一定的误判概率,如 布隆过滤器可能会错误地认为某个商品存在,但实际上并不存在。但误判只会导致查询数据库,且不会导致缓存穿透。
缓存雪崩
缓存雪崩 指的是大量缓存同时失效或宕机,导致大量请求直接访问数据库,从而使数据库承受极大的压力,甚至可能导致数据库崩溃,系统性能严重下降
缓存雪崩的两种情况:
-
大量缓存同时过期
当缓存中存储的数据的过期时间被设置为相同的时刻 (比如固定的时点、相同的时间段等),所有缓存的数据会在同一时间失效。这时,大量的请求将同时访问数据库,导致数据库瞬间承受极大的压力。
例如,系统中所有商品信息的缓存都设置了相同的过期时间,如每天凌晨12点。凌晨12点过后,大量商品的缓存失效,用户在此时查询商品时都会绕过缓存直接访问数据库,数据库的负载突然激增,可能会导致数据库崩溃。
影响:
*- 数据库的负载骤增 ,可能会导致响应慢、数据库崩溃、甚至系统瘫痪。
-
- 请求超时,性能瓶颈显现
解决方案: 设置不同的缓存过期时间(随机过期时间)
为了避免大量缓存项在同一时刻过期,可以为不同缓存项设置随机的过期时间,避免大量缓存同时失效。
例如,可以设置每个缓存项的过期时间为原定过期时间加上一段随机的时间。商品信息的缓存设置为 原本的过期时间 + 随机[0, 60]秒,避免缓存集中在同一时刻失效。
-
缓存系统宕机
缓存系统 Redis 发生故障或宕机,导致缓存无法访问,系统中的所有请求都会绕过缓存,直接访问数据库。这样一来,数据库可能会瞬间承受比平时多得多的访问压力。
影响: 数据库性能下降 ,在缓存失效或宕机时,系统会直接对数据库产生巨大的压力,尤其是高并发的请求会导致数据库响应延迟增加 ,甚至可能导致数据库崩溃。解决方案:
- 缓存集群
通过搭建缓存集群 ,可以确保缓存系统在某些节点宕机时,其他节点仍然可以提供服务。
这样不仅可以 提高系统的容错能力,还 保证了缓存系统的高可用性,即使某些缓存节点宕机,其他节点也可以继续提供缓存服务。
- 缓存降级机制
当缓存系统出现故障时,系统会自动切换到降级模式,直接从数据库中查询数据,从而 减少因缓存系统宕机导致的性能下降,避免因为缓存不可用导致整个系统瘫痪。
- 多级缓存
采用多级缓存结构,当第一级缓存(例如 Redis)宕机时,可以自动切换到第二级缓存(例如本地缓存或其他缓存服务),确保缓存依然可用。
这样 可以快速响应缓存失败时的请求,避免全盘查询数据库,还可以增强系统的容错性,减少因缓存系统宕机导致的性能下降
缓存击穿
缓存击穿问题(热点 Key 问题)是指当缓存中的某个热点数据失效时,多个请求并发访问这个数据,并且每个请求都直接访问数据库,绕过缓存,造成数据库的高负载甚至崩溃。
解决方案:
-
互斥锁
互斥锁 的核心思想是使用锁来控制并发请求,在缓存失效时,通过加锁机制确保只有一个请求能够查询数据库并更新缓存,其他请求则需要等待或使用缓存中的旧数据。这样可以避免多个并发请求同时访问数据库,从而减少数据库压力和防止性能问题。优点:
- 避免数据库负载过大:确保同一时刻只有一个请求查询数据库,其他请求需要等待或返回空数据。
- 保证一致性 :只有一个请求去更新缓存,避免了缓存更新不一致的问题。
缺点: - 性能开销:如果并发量很大,锁的争用可能导致延迟和性能下降,增加了等待时间
-
逻辑过期
逻辑过期 是通过将缓存的过期时间设置为"逻辑上的过期",即缓存数据并不会立即过期,而是采用某种策略延迟处理过期的状态。这样,缓存可以保持有效一段时间,直到某个请求去数据库更新数据为止。它的核心思想是"延迟失效"或"惰性更新",让数据在缓存中继续有效,直到有请求真正需要更新缓存,从而减少频繁的数据库查询,并提升缓存的命中率。
优点:- 减少数据库压力:通过延迟更新缓存,避免在每次请求时都去数据库查询,减少了数据库的访问压力。
- 提高缓存命中率:缓存会在逻辑过期期间继续使用,直到真正需要更新缓存时才会查询数据库。
- 性能较高:相比互斥锁,逻辑过期的开销较小,能够提高系统的响应速度和吞吐量,尤其在高并发场景下。
缺点:
- 可能有些数据不准确:因为数据有一个逻辑过期时间,可能会导致缓存中的数据存在"过期"状态,影响数据的实时性。
- 需要精确控制过期时间:合理设置逻辑过期时间,避免缓存中的数据被过久地延迟更新