4. 契约概念详解:API契约 vs 缓存契约、契约的组成要素
在缓存版本校验体系中,“契约”是连接业务逻辑与缓存存储的核心约束。它定义了数据在缓存中如何组织、如何识别、以及如何失效。理解契约的本质,是设计可校验、可演进缓存系统的前提。
4.1 API契约 vs 缓存契约
许多团队熟悉API契约(如OpenAPI、gRPC Proto),但容易混淆它与缓存契约的职责边界。两者虽然都用于约定数据格式,但关注点截然不同:
| 对比维度 | API契约 | 缓存契约 |
|---|---|---|
| 核心目标 | 定义服务间通信的接口规范 | 定义缓存中数据的存储与校验规则 |
| 作用阶段 | 请求/响应时 | 缓存写入、读取、版本校验时 |
| 版本载体 | URL路径、Header、Schema版本号 | Key后缀、Value内嵌版本字段、TTL策略 |
| 校验时机 | 编译期或运行时请求校验 | 缓存命中后、反序列化前 |
| 变更影响 | 可能导致客户端调用失败 | 可能导致缓存穿透、数据错乱 |
| 典型工具 | OpenAPI、gRPC、Thrift | 自定义版本号、序列化Schema、TTL策略 |
关键认知:API契约解决的是“接口兼容性”,缓存契约解决的是“数据兼容性”。一个服务升级API版本时,如果不同步更新缓存契约,旧缓存数据可能被新代码错误解析,导致静默数据损坏。
4.2 缓存契约的三大组成要素
一个完整的缓存契约由三个要素构成,缺一不可:Key模式、Value结构、TTL策略。三者共同决定了缓存数据的可识别性、可解析性和时效性。
4.2.1 Key模式:缓存数据的“身份证”
Key模式定义了缓存键的命名规则,是缓存契约的入口。它必须包含足够的信息来唯一标识一条缓存数据,同时支持版本路由。
推荐结构:
{业务域}:{实体类型}:{版本标识}:{业务主键}
# 示例
user:profile:v2:9527
product:detail:v3:sku_10086
order:list:v1:20240101
Key模式设计原则:
- 版本前置:将版本标识放在业务主键之前,便于按版本前缀批量扫描或删除。
- 命名空间隔离:使用业务域作为前缀,避免不同模块Key冲突。
- 可读性:使用冒号分隔,符合Redis官方最佳实践。
- 版本号编码:推荐使用整数递增(v1、v2)或语义化版本(v2_1_0),避免特殊字符。
4.2.2 Value结构:缓存数据的“骨架”
Value结构定义了缓存数据的序列化格式和字段约束。它是版本校验的核心战场——不同版本的数据可能字段不同、类型不同、甚至序列化方式不同。
常见Value结构方案:
| 方案 | 描述 | 版本校验方式 | 适用场景 |
|---|---|---|---|
| 内嵌版本字段 | 在Value中增加version字段 | 读取时比对version值 | JSON/Map结构,灵活 |
| Schema ID | 使用Avro/Protobuf Schema ID | 反序列化时匹配Schema | 强类型、高性能场景 |
| 包装对象 | Value外层包裹版本+数据+校验和 | 先解析外层,再校验版本 | 多版本共存、灰度发布 |
| 无版本 | 纯业务数据,无版本标记 | 依赖Key中的版本标识 | 简单场景,不推荐 |
推荐实践:采用“Key版本 + Value内嵌版本”双保险策略。Key版本用于快速路由,Value内嵌版本用于防御性校验,防止错误写入或缓存污染。
4.2.3 TTL策略:缓存数据的“保质期”
TTL(Time To Live)是缓存契约中容易被忽视但至关重要的要素。它决定了缓存数据的有效时长,直接影响版本切换的平滑度。
TTL策略设计要点:
- 版本切换窗口:新版本上线后,旧版本缓存仍在TTL内存活。TTL越长,新旧数据共存期越长。
- 渐进式过期:大版本升级时,可主动缩短旧版本Key的TTL,加速数据淘汰。
- 热点数据保护:对于高频访问数据,TTL不宜过短,否则缓存穿透压力大。
- 版本回滚:TTL策略需支持快速回滚——回滚后旧版本数据应能立即被新请求使用。
# TTL策略示例
# 新版本上线时,旧版本TTL从3600s缩短至300s
SET user:profile:v1:9527 "{...}" EX 300
SET user:profile:v2:9527 "{...}" EX 3600
4.3 契约组成要素关系图
下图展示了缓存契约三大要素如何协同工作,构成完整的版本校验闭环:
4.4 契约版本校验的完整流程
将三大要素串联起来,就形成了缓存契约版本校验的完整闭环:
- 写入阶段:根据当前业务版本,生成带版本标识的Key,序列化Value(内嵌版本号),设置合理的TTL。
- 读取阶段:按Key模式构造查询Key,命中后反序列化Value,提取版本号与期望版本比对。
- 版本匹配:版本一致 → 直接返回;版本不一致 → 触发缓存重建。
- 缓存重建:回源数据库,按新版本契约重新写入缓存,更新TTL。
- 版本切换:新版本上线后,旧版本Key自然过期或被主动淘汰,完成平滑迁移。
常见陷阱:只关注Key模式而忽略Value结构校验。例如Key中携带v2,但Value实际是v1格式的数据(由旧版本代码写入)。此时必须通过Value内嵌版本进行二次校验,否则会导致反序列化异常。
4.5 契约版本演进策略
随着业务迭代,缓存契约需要不断演进。以下是三种常见的版本演进策略:
| 策略 | Key模式变化 | Value结构变化 | TTL策略 | 推荐场景 |
|---|---|---|---|---|
| 原地升级 | Key不变 | 兼容新增字段 | TTL不变 | 小版本、字段追加 |
| 版本分流 | Key增加版本后缀 | 可完全不兼容 | 旧版本TTL缩短 | 大版本、字段删除/类型变更 |
| 双写双读 | 新旧Key同时存在 | 两套结构共存 | 新版本TTL更长 | 灰度发布、A/B测试 |