2. 缓存基础理论:缓存的作用与价值、常见缓存策略(LRU、LFU、TTL)、缓存穿透/击穿/雪崩

2.1 缓存的核心作用与业务价值

在 RBAC 权限系统中,缓存并非锦上添花,而是决定系统能否承载高并发访问的关键基础设施。权限数据的读取频率远高于写入频率,且每次鉴权都需要跨多个数据表(用户-角色-权限)进行关联查询,若直接压入数据库,系统会在极短时间内崩溃。

维度 无缓存场景(直接查库) 有缓存场景(Redis / 本地缓存)
单次鉴权延迟 10 ~ 50 ms(多表 JOIN) 0.1 ~ 1 ms(内存读取)
数据库 QPS 瓶颈 约 5,000 ~ 10,000 可支撑 100,000+(缓存扛量)
热点数据压力 全部落在数据库 缓存吸收 95%+ 请求
RBAC 典型场景 每次请求都查用户权限表 权限快照缓存,TTL 内零数据库查询
核心结论: 缓存的价值在于“用空间换时间”,将热数据从慢速存储(磁盘/数据库)提升到快速存储(内存),从而降低延迟、提升吞吐、保护后端数据库不被击穿。

2.2 常见缓存策略:LRU、LFU、TTL

缓存不是无限大的,当内存达到上限时,必须淘汰旧数据。不同的淘汰策略直接影响缓存命中率和系统行为。在 RBAC 水位复核中,我们最常与 LRU 和 TTL 打交道。

2.2.1 LRU(Least Recently Used)—— 最近最少使用

  • 原理: 淘汰最长时间未被访问的数据。基于“时间局部性”假设:如果数据最近被访问过,未来被访问的概率也更高。
  • 实现方式: 双向链表 + 哈希表(O(1) 查找与淘汰)。
  • 适用场景: 用户会话缓存、通用数据缓存,访问模式有明显的热点变化。
  • RBAC 场景: 适合缓存用户权限列表,因为活跃用户的权限会被频繁访问,而长期不活跃用户的权限自然被淘汰。

2.2.2 LFU(Least Frequently Used)—— 最不经常使用

  • 原理: 淘汰访问频率最低的数据。基于“频率局部性”假设:访问次数多的数据未来更可能被再次访问。
  • 实现方式: 维护每个 key 的访问计数器,淘汰时选择计数最小的 key。
  • 适用场景: 热点数据相对固定、访问频率差异明显的场景。
  • RBAC 场景: 适合缓存全局角色定义、权限码表等“几乎不变”的元数据。

2.2.3 TTL(Time To Live)—— 过期时间

  • 原理: 为每个缓存 key 设置一个生存时间,到期后自动失效。这不是淘汰策略,而是数据一致性策略。
  • 实现方式: Redis 的 EXPIRE 命令、本地缓存的定时扫描。
  • 适用场景: 所有需要保证数据最终一致性的场景。
  • RBAC 场景: 权限变更后,通过缩短 TTL 或主动失效来刷新缓存,避免用户持有过期权限。
策略 淘汰依据 内存开销 RBAC 推荐用途
LRU 最后访问时间 中(链表指针) 用户权限快照
LFU 访问频率 高(计数器) 角色/权限元数据
TTL 时间 低(时间戳) 所有缓存(配合使用)
实战建议: 在 RBAC 缓存设计中,不要单独依赖 LRU 或 LFU。标准做法是:LRU + TTL 组合。LRU 控制内存水位,TTL 保证数据最终一致性。当权限发生变更时,优先使用主动失效(delete key),而不是等待 TTL 自然过期。
缓存淘汰策略对比:LRU vs LFU vs TTL LRU 最近最少使用 淘汰依据:最后访问时间 数据结构:双向链表+哈希表 时间复杂度:O(1) 适用:用户权限快照 活跃用户保留 不活跃用户淘汰 LFU 最不经常使用 淘汰依据:访问频率 数据结构:最小堆/计数器 时间复杂度:O(log n) 适用:角色/权限元数据 高频热点保留 低频冷数据淘汰 TTL 过期时间 淘汰依据:时间 数据结构:时间戳/定时器 实现:EXPIRE / 惰性删除 适用:所有缓存(配合使用) 到期自动失效 保证最终一致性 RBAC 推荐组合:LRU(控制内存水位)+ TTL(保证数据一致性)+ 主动失效(权限变更时)

2.3 缓存三大经典问题:穿透、击穿、雪崩

这三个问题是缓存系统的“三大灾难”,在 RBAC 高并发鉴权场景中尤为致命。理解它们的成因和解决方案,是进行缓存水位复核的前提。

2.3.1 缓存穿透

  • 定义: 请求查询一个根本不存在的数据(如一个不存在的用户 ID),缓存中没有,数据库也没有,每次请求都直接打到数据库。
  • 危害: 恶意攻击者可以利用不存在的 key 发起大量请求,直接穿透缓存打垮数据库。
  • 解决方案:
    1. 缓存空对象: 即使数据库查询为空,也缓存一个空值(如 null),并设置较短的 TTL(如 30~60 秒)。
    2. 布隆过滤器: 在缓存之前加一层布隆过滤器,判断 key 是否可能存在,不存在则直接拒绝。

2.3.2 缓存击穿

  • 定义: 一个热点 key 在缓存失效的瞬间,大量并发请求同时涌入,全部穿透到数据库。
  • 危害: 数据库瞬间被热点请求打爆,导致服务不可用。
  • 解决方案:
    1. 互斥锁(Mutex): 只允许一个线程去数据库加载数据,其他线程等待或返回旧缓存。
    2. 逻辑过期: 缓存中不设置 TTL,而是存储一个逻辑过期时间,后台异步刷新。
    3. 永不过期 + 定时刷新: 热点 key 永不过期,由后台任务定期更新。

2.3.3 缓存雪崩

  • 定义: 大量缓存 key 在同一时间集中过期,或者缓存节点宕机,导致大量请求直接打到数据库。
  • 危害: 数据库压力瞬间飙升,可能引发级联故障,导致整个系统雪崩。
  • 解决方案:
    1. TTL 随机化: 在基础 TTL 上增加一个随机偏移量(如 ±10%),避免集中过期。
    2. 缓存高可用: Redis 集群、哨兵模式,避免单点故障。
    3. 限流降级: 当缓存大面积失效时,对数据库请求进行限流,保护后端。
    4. 多级缓存: 本地缓存(Caffeine)+ 分布式缓存(Redis)组合,降低单层失效风险。
问题 根因 典型表现 RBAC 场景举例 首选解决方案
穿透 查询不存在的数据 DB 空查询激增 恶意遍历不存在的用户 ID 布隆过滤器 + 空值缓存
击穿 热点 key 失效瞬间 单 key 打爆 DB 超级管理员权限缓存失效 互斥锁 / 逻辑过期
雪崩 大量 key 同时失效 / 节点宕机 DB 全面崩溃 所有角色缓存 TTL 同时到期 TTL 随机化 + 多级缓存
⚠️ 水位复核重点关注: 在 RBAC 系统中,缓存雪崩是最容易被忽视的风险。很多团队只设置了统一的 TTL(如 3600 秒),导致所有权限缓存在同一秒过期。必须检查 TTL 是否添加了随机偏移,以及是否配置了本地缓存作为第二道防线。
缓存三大问题解决方案架构图 用户请求 布隆过滤器 (穿透防护) 不存在则直接拒绝 缓存层 本地缓存 (Caffeine) 分布式缓存 (Redis) 互斥锁(击穿防护) TTL 随机化(雪崩防护) 数据库 限流降级 三层防护体系 第一层:布隆过滤器(拦截穿透) 第二层:多级缓存 + 互斥锁 + TTL 随机化(防御击穿与雪崩) 第三层:限流降级(兜底保护数据库)

2.4 本节小结

  • 缓存的核心价值在于降低延迟、提升吞吐、保护数据库,在 RBAC 系统中是承载高并发鉴权的基石。
  • LRU 适合用户权限快照,LFU 适合角色元数据,TTL 是所有缓存必须配合使用的数据一致性策略。
  • 缓存三大问题——穿透、击穿、雪崩——在 RBAC 场景中都有对应的典型表现,必须通过布隆过滤器、互斥锁、TTL 随机化、多级缓存等手段进行系统性防御。
  • 在后续的缓存水位复核中,我们将重点检查 TTL 配置是否合理、是否存在热点 key 集中过期、以及空值缓存是否被正确启用。