2. Redis缓存基础

在构建缓存一致性接口契约之前,必须扎实掌握 Redis 作为缓存层的三大基础支柱:数据结构决定了能缓存什么形态的数据;读写模式决定了应用与缓存之间的交互范式;过期策略决定了缓存数据的生命周期与自动清理机制。三者共同构成了缓存一致性设计的底层约束。

2.1 Redis 核心数据结构回顾

Redis 提供了多种数据结构,但在缓存场景中,最常用的五种结构及其适用场景如下表所示。选择正确的数据结构,能显著降低缓存一致性维护的复杂度。

数据结构 底层编码 典型缓存场景 一致性关注点
String int / embstr / raw 会话 Token、对象 JSON 快照、计数器 全量覆盖,更新需原子性
Hash ziplist / hashtable 用户属性、商品详情(字段级缓存) 部分更新,避免整条覆盖
List quicklist 消息队列、最新动态列表 顺序保证,防止重复消费
Set intset / hashtable 标签系统、去重白名单 集合运算的原子性
Sorted Set ziplist / skiplist 排行榜、延时队列、限流窗口 分数与成员的一致性
接口契约启示:在定义缓存接口时,应明确指定使用哪种数据结构。例如,用户信息缓存应声明为 Hash 而非 String,以便下游只更新部分字段时不会破坏整个缓存对象。

2.2 缓存读写模式

缓存读写模式决定了数据在 Redis 与数据库之间的流动路径。不同的模式对一致性保证的强度、实现复杂度、性能开销有直接影响。以下是三种主流模式的对比。

模式 读路径 写路径 一致性强度 典型场景
Cache-Aside 读缓存 → 未命中读 DB → 回填缓存 写 DB → 删除缓存 最终一致性(有窗口期) 通用业务缓存
Read-Through 缓存组件自动回填(应用无感) 写 DB → 缓存失效 最终一致性 数据访问层封装
Write-Through 同 Read-Through 写缓存 → 同步写 DB 强一致性(写路径) 金融、配置中心
接口契约关键决策:在 Cache-Aside 模式下,写操作必须“先写数据库,后删缓存”,这是避免脏数据的基础规则。接口契约应强制要求调用方遵循此顺序,并提供幂等删除接口。

以下 SVG 展示了 Cache-Aside 模式的完整读写流程,这是本课程后续所有一致性方案的基础参考模型。

Cache-Aside 读写模式流程图 Cache-Aside 读写模式 应用服务 Redis 缓存 数据库 (DB) ① 读缓存 ② 未命中 (Miss) ③ 读 DB ④ 回填缓存 ⑤ 写 DB ⑥ 删缓存 关键规则 • 读:先查缓存,未命中则查 DB 并回填,回填时需设置 TTL。 • 写:先更新 DB,再删除缓存(延迟双删策略在此基础上增加二次删除)。 • 删除缓存失败时,需通过重试或异步补偿保证最终一致性。 • 接口契约应约定:写接口返回后,调用方不应立即读取缓存。

2.3 缓存过期策略

Redis 的过期策略决定了键值对在何时以及如何被淘汰。理解这些策略对于设计缓存一致性接口至关重要——过期策略直接影响缓存与数据库之间数据不一致的窗口大小。

2.3.1 三种过期删除策略

策略名称 工作机制 CPU 开销 内存开销 一致性影响
定时删除 设置 TTL 时创建定时器,到期立即删除 高(每个键一个定时器) 低 窗口期最短
惰性删除 访问键时检查是否过期,过期则删除 低(仅访问时触发) 高(过期键可能长期驻留) 窗口期最长
定期删除 每秒执行有限次扫描,随机抽查并删除过期键 中(可配置频率) 中 窗口期可控
接口契约陷阱:Redis 实际采用“定期删除 + 惰性删除”的组合策略。这意味着一个已过期的键可能不会立即被删除。如果接口契约依赖“过期即不可见”的语义,必须考虑这个时间窗口。建议在业务层面增加逻辑过期时间(在 value 中嵌入过期时间戳),由应用主动校验。

2.3.2 内存淘汰策略(maxmemory-policy)

当 Redis 内存达到上限时,淘汰策略决定哪些键被移除。以下是与缓存一致性最相关的四种策略。

策略 淘汰范围 适用场景 一致性风险
allkeys-lru 所有键 通用缓存,热点数据优先保留 低(淘汰最近最少使用)
volatile-lru 仅设置了 TTL 的键 混合存储(持久键 + 缓存键) 中(持久键永不淘汰)
allkeys-lfu 所有键 访问频率差异极大的场景 低(淘汰最不经常使用)
volatile-ttl 仅设置了 TTL 的键 优先淘汰即将过期的键 中(TTL 短的键先被淘汰)
接口契约建议:对于缓存一致性要求高的接口,应使用 allkeys-lru 策略,并确保所有缓存键都设置了合理的 TTL。避免使用 noeviction(不淘汰,写操作直接报错),否则在内存满时会导致缓存写入失败,进而引发缓存雪崩。

以下 SVG 展示了 Redis 过期策略与内存淘汰策略的协同工作流程,帮助理解键的生命周期管理。

Redis 过期与淘汰策略协同流程 Redis 过期与淘汰策略协同流程 客户端写入键 有 TTL? 加入过期字典 直接存储 定期扫描删除 惰性删除(访问时) 内存淘汰 (LRU/LFU) 策略组合 • 定期删除:默认每秒扫描 10 次,每次抽查 20 个键 • 惰性删除:访问时触发

2.4 基础总结与接口契约映射

本章的三个基础知识点直接映射到缓存一致性接口契约的设计决策:

  • 数据结构选择 → 接口应声明缓存的数据结构类型,避免调用方误用。例如:@CacheStore(type=Hash, key="user:{id}")。
  • 读写模式约定 → 接口契约必须明确写操作的顺序(先 DB 后缓存),并提供缓存删除/更新的幂等接口。
  • 过期策略配置 → 接口应约定 TTL 的最小值、最大值以及默认值,并告知调用方“过期后数据不可用”的精确语义。
实践检查清单:在定义缓存接口契约前,请确认以下问题:
1. 缓存的数据结构是否与业务字段结构匹配?
2. 写操作是否严格遵循“先 DB 后缓存”的顺序?
3. 是否所有缓存键都设置了合理的 TTL?
4. 是否配置了 allkeys-lru 淘汰策略?
5. 是否在应用层实现了逻辑过期时间作为兜底?