文章

面试题学习笔记 | Redis 缓存

我们都知道,由于 Redis 采用内存存储,并且数据访问速度极快,因此常被用作后端缓存,以降低频繁查询数据库带来的流量压力。同时,缓存能够显著提升数据查询效率,因此在高并发场景中被广泛应用。

然而,缓存的引入使得数据操作从单一数据库查询变成了多步操作,导致系统失去了原子性,这也带来了 数据一致性 问题。此外,Redis 作为缓存,在一些场景下会出现常见的 缓存失效、击穿、穿透、雪崩 等问题,而这些正是面试中的高频考点。


Redis 中如何保证缓存与数据库的数据一致性?

在引入缓存后,为了保障系统的正确性,我们需要确保 缓存与数据库的数据一致性,特别是在数据更新时,需要采取一定策略,避免缓存和数据库数据不同步的情况。

通常来说,我们可以想到以下三种方案:

  1. 先更新缓存,再更新数据库

  2. 先更新数据库,再更新缓存

  3. 先删除缓存,再更新数据库,等待查询时将数据库数据重新回填缓存

这三种方式看似合理,但在并发环境下,都会遇到严重的一致性问题。

方案分析

1. 先更新缓存,再更新数据库

这看起来是一个不错的方案:先更新缓存,使后续请求能够直接获取最新数据,然后再更新数据库。然而,在 分布式环境 下,由于 网络延迟请求乱序,可能会导致数据库和缓存不同步。例如:

  • 请求 A 先到达,更新缓存,值变为 x

  • 请求 B 后到达,更新缓存,值变为 y

  • 由于网络问题,请求 B 先更新数据库,数据库变为 y

  • 请求 A 更新数据库,数据库最终变为 x

最终:缓存为 y,数据库为 x,数据不一致。

2. 先更新数据库,再更新缓存

这个方案和上一种方案类似,也面临相同的并发问题。由于 网络延迟,更新数据库后更新缓存的操作可能会被后来的请求覆盖,导致缓存和数据库数据不一致。

3. 先删除缓存,再更新数据库

这种方式在并发环境下同样会导致数据不一致,例如:

  • 请求 A(更新操作): 先删除缓存

  • 请求 B(查询操作): 发现缓存为空,去数据库查询到旧值

  • 请求 B 将旧值回写缓存

  • 请求 A 更新数据库的新值

最终:数据库是新值,缓存是旧值,数据不一致。


可靠的缓存一致性方案

在生产环境中,通常采用以下 三种可行方案 来保证数据库和缓存的一致性:

1. 先更新数据库,再删除缓存

更新数据库后,删除缓存,等待下次查询时重新加载数据库数据。这样做的好处是,即使缓存删除失败,数据仍然是最新的。该方案可以保证最终一致性。

但仍然可能会有 缓存不一致 的极端情况,比如:

  • 请求 A(查询操作):缓存失效,查询数据库获得旧值

  • 请求 B(更新操作):删除缓存,更新数据库

  • 请求 A 将旧值回写缓存

为了解决这个问题,可以结合 缓存双删策略

2. 缓存双删策略

该方案优化了 "先删除缓存,再更新数据库" 方案的缺陷,具体做法是:

  1. 在更新数据库前,先删除缓存

  2. 更新数据库

  3. 在一定时间后,再次删除缓存

这样可以防止查询请求回写旧值。延迟删除可以通过 消息队列、定时任务延迟任务 实现。

3. Binlog 异步更新缓存

该方案是 数据库驱动缓存更新,其核心思想是 监听数据库的 Binlog 变更,并通过 异步事件 更新缓存。流程如下:

  1. 数据库更新数据

  2. 中间件(如 Canal)监听 Binlog

  3. 消息队列异步通知缓存进行更新

该方案的优势在于 高可靠性,但需要确保 顺序消费,并增加 重试机制,避免因网络问题导致缓存更新失败。

补充方案:使用分布式锁

为了确保 强一致性,可以使用 分布式读写锁,如 Redis 的 RedLock 机制:

  • 查询缓存时加 读锁

  • 更新数据库时加 写锁(写锁和读写操作互斥)

  • 更新数据库后,删除缓存

  • 由于写锁互斥,在更新完成前,读请求无法访问旧缓存

但此方案 牺牲了性能,仅适用于强一致性要求的场景。


Redis 中的缓存击穿、缓存穿透和缓存雪崩

在实际业务场景中,Redis 可能会出现 缓存击穿、缓存穿透和缓存雪崩 这三大问题。

1. 缓存击穿

现象:指某个热点数据在缓存中失效,大量请求直接访问数据库,可能会导致数据库宕机。

解决方案

  • 热点数据永不过期(逻辑过期 + 异步更新)

  • 使用互斥锁,保证只有一个请求查询数据库并更新缓存

2. 缓存穿透

现象:指查询一个数据库不存在的数据,导致每次请求都要访问数据库。例如:用户查询 id=99999999 的数据,但数据库里并没有这条记录。

解决方案

  • 使用布隆过滤器 过滤掉不存在的数据

  • 缓存空值(如果数据库返回 null,也缓存该值,并设置较短的过期时间)

3. 缓存雪崩

现象:指大量缓存数据同时过期,导致数据库负载激增。

解决方案

  • 随机过期时间 避免大量数据同时失效

  • 多级缓存(如使用本地缓存 + Redis)

  • 请求限流,避免数据库瞬间压力过大


总结

在引入 Redis 作为缓存后,我们需要关注 数据一致性问题,避免缓存与数据库不同步。同时,还要防范 缓存击穿、穿透和雪崩 等常见问题,以保证系统的高可用性和稳定性。不同的业务场景需要结合自身需求选择合适的缓存更新策略,权衡 一致性性能,确保系统在高并发环境下依然能够高效运行

License:  CC BY 4.0