面试题学习笔记 | Redis 缓存
我们都知道,由于 Redis 采用内存存储,并且数据访问速度极快,因此常被用作后端缓存,以降低频繁查询数据库带来的流量压力。同时,缓存能够显著提升数据查询效率,因此在高并发场景中被广泛应用。
然而,缓存的引入使得数据操作从单一数据库查询变成了多步操作,导致系统失去了原子性,这也带来了 数据一致性 问题。此外,Redis 作为缓存,在一些场景下会出现常见的 缓存失效、击穿、穿透、雪崩 等问题,而这些正是面试中的高频考点。
Redis 中如何保证缓存与数据库的数据一致性?
在引入缓存后,为了保障系统的正确性,我们需要确保 缓存与数据库的数据一致性,特别是在数据更新时,需要采取一定策略,避免缓存和数据库数据不同步的情况。
通常来说,我们可以想到以下三种方案:
先更新缓存,再更新数据库
先更新数据库,再更新缓存
先删除缓存,再更新数据库,等待查询时将数据库数据重新回填缓存
这三种方式看似合理,但在并发环境下,都会遇到严重的一致性问题。
方案分析
1. 先更新缓存,再更新数据库
这看起来是一个不错的方案:先更新缓存,使后续请求能够直接获取最新数据,然后再更新数据库。然而,在 分布式环境 下,由于 网络延迟 或 请求乱序,可能会导致数据库和缓存不同步。例如:
请求 A 先到达,更新缓存,值变为
x
请求 B 后到达,更新缓存,值变为
y
由于网络问题,请求 B 先更新数据库,数据库变为
y
请求 A 更新数据库,数据库最终变为
x
最终:缓存为 y
,数据库为 x
,数据不一致。
2. 先更新数据库,再更新缓存
这个方案和上一种方案类似,也面临相同的并发问题。由于 网络延迟,更新数据库后更新缓存的操作可能会被后来的请求覆盖,导致缓存和数据库数据不一致。
3. 先删除缓存,再更新数据库
这种方式在并发环境下同样会导致数据不一致,例如:
请求 A(更新操作): 先删除缓存
请求 B(查询操作): 发现缓存为空,去数据库查询到旧值
请求 B 将旧值回写缓存
请求 A 更新数据库的新值
最终:数据库是新值,缓存是旧值,数据不一致。
可靠的缓存一致性方案
在生产环境中,通常采用以下 三种可行方案 来保证数据库和缓存的一致性:
1. 先更新数据库,再删除缓存
即 更新数据库后,删除缓存,等待下次查询时重新加载数据库数据。这样做的好处是,即使缓存删除失败,数据仍然是最新的。该方案可以保证最终一致性。
但仍然可能会有 缓存不一致 的极端情况,比如:
请求 A(查询操作):缓存失效,查询数据库获得旧值
请求 B(更新操作):删除缓存,更新数据库
请求 A 将旧值回写缓存
为了解决这个问题,可以结合 缓存双删策略。
2. 缓存双删策略
该方案优化了 "先删除缓存,再更新数据库" 方案的缺陷,具体做法是:
在更新数据库前,先删除缓存
更新数据库
在一定时间后,再次删除缓存
这样可以防止查询请求回写旧值。延迟删除可以通过 消息队列、定时任务 或 延迟任务 实现。
3. Binlog 异步更新缓存
该方案是 数据库驱动缓存更新,其核心思想是 监听数据库的 Binlog 变更,并通过 异步事件 更新缓存。流程如下:
数据库更新数据
中间件(如 Canal)监听 Binlog
消息队列异步通知缓存进行更新
该方案的优势在于 高可靠性,但需要确保 顺序消费,并增加 重试机制,避免因网络问题导致缓存更新失败。
补充方案:使用分布式锁
为了确保 强一致性,可以使用 分布式读写锁,如 Redis 的 RedLock 机制:
查询缓存时加 读锁
更新数据库时加 写锁(写锁和读写操作互斥)
更新数据库后,删除缓存
由于写锁互斥,在更新完成前,读请求无法访问旧缓存
但此方案 牺牲了性能,仅适用于强一致性要求的场景。
Redis 中的缓存击穿、缓存穿透和缓存雪崩
在实际业务场景中,Redis 可能会出现 缓存击穿、缓存穿透和缓存雪崩 这三大问题。
1. 缓存击穿
现象:指某个热点数据在缓存中失效,大量请求直接访问数据库,可能会导致数据库宕机。
解决方案:
热点数据永不过期(逻辑过期 + 异步更新)
使用互斥锁,保证只有一个请求查询数据库并更新缓存
2. 缓存穿透
现象:指查询一个数据库不存在的数据,导致每次请求都要访问数据库。例如:用户查询 id=99999999
的数据,但数据库里并没有这条记录。
解决方案:
使用布隆过滤器 过滤掉不存在的数据
缓存空值(如果数据库返回
null
,也缓存该值,并设置较短的过期时间)
3. 缓存雪崩
现象:指大量缓存数据同时过期,导致数据库负载激增。
解决方案:
随机过期时间 避免大量数据同时失效
多级缓存(如使用本地缓存 + Redis)
请求限流,避免数据库瞬间压力过大
总结
在引入 Redis 作为缓存后,我们需要关注 数据一致性问题,避免缓存与数据库不同步。同时,还要防范 缓存击穿、穿透和雪崩 等常见问题,以保证系统的高可用性和稳定性。不同的业务场景需要结合自身需求选择合适的缓存更新策略,权衡 一致性 和 性能,确保系统在高并发环境下依然能够高效运行