面试问题

什么是缓存击穿、穿透、雪崩?区别是什么?如何解决?

  • 穿透:请求永远不会命中缓存
    • 请求的数据缓存中没有,数据库也没有,每次请求都会打到数据库
    • 查不存在的用户ID,如 ID=-1
    • ① 参数校验 ② 布隆过滤器 ③ 缓存空值
      • 启动时将数据库中所有合法的 ID 加入布隆过滤器;
      • 每次查询前先判断:ID 是否在布隆过滤器中?
      • 如果不在 → 直接返回,不查缓存也不查库;

如果在 → 走正常的缓存查询流程。

  • 击穿:缓存偶尔失效后瞬间打爆后端
    • 某个热点数据突然过期,大量请求同时查询,穿透缓存打爆数据库
    • 秒杀商品库存刚好过期
    • ① 加互斥锁 ② 提前续期 ③ 永不过期+异步更新
  • 雪崩:大量缓存同时失效导致系统崩溃
    • 大量缓存同一时间过期,所有请求打到数据库,系统瞬间崩溃
    • 定时批量缓存设置相同过期时间
    • ① 加随机过期时间 ② 热点预热 ③ 多级缓存

      布隆过滤器是一种概率型数据结构,可以用来判断一个元素一定不存在/可能存在

Redis的过期删除策略?

  • 惰性删除:访问key时,才检查他是否过期,如果过期就删除 | 但可能留下大量垃圾
  • 定期删除:每隔一段时间随机抽取部分 key 检查过期并删除(如每秒检查 20 个)
  • 主动淘汰:当内存不足时触发内存淘汰机制

什么是基于 Redis 构造分布式锁?

  • 本质:利用 Redis 的 SET key value NX EX 原子命令,保证同一时刻只有一个线程/服务获取到锁,从而控制并发访问共享资源(如库存扣减、优惠券发放)。
1
2
// 尝试加锁
SET lock_key my_value NX EX 10
  • NX:只在 key 不存在时设置(互斥)
  • EX 10:自动过期(防死锁)
  • my_value:用 UUID 表示“我是谁”,用于后续解锁校验

为什么用 Lua 脚本做分布式锁?

加锁与解锁需要“判断 + 删除”是一个原子操作。用普通 Redis 命令不能保证原子性(可能判断完刚要删,锁就被别人改了)。
Redis 支持执行一整段 Lua 脚本,这段脚本在 Redis 内部一次性、原子性执行,所以我们用它封装加锁或解锁逻辑,确保线程安全。

1
2
3
4
5
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end