缓存模式与分布式锁
Redis 最广泛的应用是缓存。用得好能扛住高并发,用不好会引发数据不一致与雪崩。本章讲清模式与陷阱。
缓存旁路模式(Cache Aside)
最常用的模式,读时先查缓存、未命中查库并回填;写时更新数据库并删除缓存。
Warning
为什么写操作是删除缓存而不是更新缓存?因为并发写时「写库顺序」与「更新缓存顺序」可能交错,导致缓存留下旧值。删除 + 下次读回填更简单可靠。先操作数据库、再删缓存(Cache Aside 的推荐顺序),并配合过期时间兜底。
延迟双删
在强一致要求下,删缓存后延迟一段时间再删一次,用来清理「读请求把旧值回填」的窗口:
三大缓存问题
缓存穿透
查询不存在的数据,缓存与数据库都未命中,请求每次打到数据库。
对策:
- 缓存空值(
SET key "" EX 60),短过期。 - 布隆过滤器(Bloom Filter)前置拦截不存在的 key。
- 参数校验,拦截非法请求。
缓存击穿
某个热点 key 过期瞬间,大量并发请求同时打到数据库。
对策:
- 互斥锁:只让一个线程回源,其余等待重试。
- 逻辑过期:value 内带过期时间,异步刷新,读永远命中。
- 热点 key 永不过期 + 后台更新。
缓存雪崩
大量 key 同一时刻集中过期,或 Redis 宕机,导致请求全部涌向数据库。
对策:
- 过期时间加随机抖动,避免集中失效。
- 多级缓存(本地缓存 + Redis)。
- Redis 高可用(哨兵/Cluster)避免单点。
- 限流与熔断,保护数据库。
分布式锁
多实例环境下,用 Redis 实现互斥。
加锁:SET NX EX
要点:必须设置过期(防死锁),value 用唯一 token(防误删他人锁)。
解锁:Lua 保证原子
Warning
不要把「判断持有者」与「删除」分成两条命令:GET 判断后、DEL 执行前锁可能已过期并被他人获取,从而误删他人锁。Lua 脚本在 Redis 中原子执行,是标准做法。
续期与 Redlock
- 长任务需要看门狗(watchdog)定期续期,如 Redisson 的实现。
- Redlock 在多个独立 Redis 节点上多数派加锁,提升容错;但其正确性在分布式系统理论下存在争议,对强一致要求极高的场景,优先使用 etcd/ZooKeeper 等基于共识的协调服务。
小结
- Cache Aside 是默认模式:读缓存回填、写库后删缓存,并设置随机过期。
- 穿透用空值/布隆过滤器,击穿用互斥锁/逻辑过期,雪崩用随机过期 + 高可用 + 限流。
- 分布式锁:
SET NX EX加锁、唯一 token、Lua 原子解锁、必要时看门狗续期。