redis的面试题

39 阅读3分钟

项目中如何保证缓存的一致性

1. 首要原则:理解业务,分级对待

强一致性场景:如商品库存、秒杀扣减、账户余额等。这类数据要求缓存和数据库必须时刻保持一致,通常需要更复杂、性能开销更大的策略

弱一致性/最终一致性场景:如用户信息、商品详情、文章内容等。这类数据允许有一个极短的时间窗口不一致,用户通常可以接受,这是我们最常遇到的场景,也是我们优化性能的主要战场。

读操作流程:
  1. 首先,从缓存中读取数据。
  2. 如果缓存命中(cache hit),直接返回数据。
  3. 如果缓存未命中(cache miss),则从数据库中读取数据。
  4. 将数据库读出的数据写入缓存,然后返回数据。

2. 应对经典问题与兜底方案

问题一:缓存穿透(Cache Penetration) 现象:大量请求查询一个数据库中根本不存在的数据(比如不存在的用户ID),导致请求直接穿透缓存,全部打到数据库上。

  1. 缓存空对象(Cache Null Object):即使从数据库没查到,也把一个空值(或特殊标记)写入缓存,并设置一个较短的过期时间(比如1-5分钟)。这样后续的请求在缓存层就被拦截了。
  2. 布隆过滤器(Bloom Filter):在缓存之前加一层布隆过滤器。它能够以极小的空间代价快速判断一个 key 是否一定不存在于数据库中。对于判断为不存在的 key,直接返回,保护后端数据库。

问题二:缓存击穿(Cache Breakdown) 现象:某个热点key在缓存过期的瞬间,同时有大量请求进来,导致所有请求都去数据库加载数据,仿佛击穿了缓存。

  1. 互斥锁(Mutex Lock):当缓存失效时,不是让所有请求都去查数据库,而是只允许一个请求(比如通过 Redis 的 SETNX 命令获取分布式锁)去数据库查询并回填缓存,其他请求等待缓存被重新填充后,再从缓存中读取。这样可以避免数据库瞬间压力过大。
  2. “永不过期”+后台更新:对热点 key 不设置过期时间,而是启动一个后台任务或利用定时任务,定期从数据库加载数据并更新缓存。这样可以完全避免缓存击穿问题,但牺牲了一定的数据实时性。

问题三:缓存雪崩(Cache Avalanche) 现象:同一时间大量缓存 key 集中失效,或者 Redis 服务宕机,导致所有请求涌向数据库,造成数据库崩溃。

  1. 差异化过期时间:给缓存设置过期时间时,增加一个随机值(例如:基础时间 + 随机1-5分钟),让key的失效时间分散开,避免同时失效。
  2. 构建高可用缓存集群:采用 Redis Sentinel(哨兵)或 Cluster(集群)模式,实现故障自动转移,避免单点故障导致的全盘崩溃。
  3. 服务降级和熔断:在系统层面,引入 Hystrix 或 Sentinel 等组件,当检测到数据库压力过大时,对非核心业务进行降级或直接熔断,保护核心业务和数据库不被拖垮。

3. 其他策略的考量

Write/Read Through:应用只和缓存交互,由缓存组件自己来维护和数据库的同步。这对应用来说更简单,但通常需要缓存中间件本身的支持(如一些云服务商提供的缓存服务)。

Write Behind:应用只更新缓存,然后缓存异步、批量地更新数据库。这种方式性能极好,但风险也最高,可能会丢失数据,一般只用于能承受数据丢失的场景,如点赞数、浏览量统计。