2. Redis 基础回顾:数据结构概览、常用命令、键空间设计原则

在深入缓存契约与版本校验之前,必须夯实 Redis 基础。本章将系统梳理 Redis 的核心数据结构、高频操作命令以及键空间设计的关键原则,为后续的契约校验实战建立坚实的知识底座。

2.1 核心数据结构概览

Redis 提供了 9 种以上数据结构,但日常缓存场景中最常用的是以下 5 种。理解它们的底层编码与适用场景,是设计高效缓存契约的前提。

数据结构 底层编码(主要) 最大元素数 典型缓存场景
String int / embstr / raw 512 MB(单 key) 会话 Token、计数器、JSON 序列化对象
Hash ziplist / hashtable 2³² - 1 个 field 对象属性缓存(如用户信息)、短结构化数据
List quicklist 2³² - 1 个元素 消息队列、时间线、最新 N 条记录
Set intset / hashtable 2³² - 1 个成员 标签系统、去重集合、好友关系
Sorted Set (ZSet) ziplist / skiplist + hashtable 2³² - 1 个成员 排行榜、延时队列、带权重的缓存排序
契约视角提示: 在缓存契约版本校验中,Hash 和 String 是最常用的载体。Hash 天然支持字段级版本标记,而 String 适合存储完整的版本化 JSON 载荷。

2.2 常用命令速查

以下命令是日常缓存操作与契约校验的基础。按功能分组列出,便于快速查阅。

2.2.1 通用命令

  • EXISTS key — 检查 key 是否存在
  • DEL key [key ...] — 删除一个或多个 key
  • EXPIRE key seconds — 设置 key 的生存时间
  • TTL key — 查看 key 剩余生存时间
  • TYPE key — 返回 key 存储的数据结构类型

2.2.2 String 命令

  • SET key value [NX|XX] [EX seconds] — 设置值,支持条件写入与过期
  • GET key — 获取值
  • MSET key value [key value ...] — 批量设置
  • MGET key [key ...] — 批量获取
  • INCR key — 原子自增 1

2.2.3 Hash 命令

  • HSET key field value — 设置单个字段
  • HGET key field — 获取单个字段
  • HMSET key field value [field value ...] — 批量设置字段
  • HGETALL key — 获取所有字段与值
  • HEXISTS key field — 检查字段是否存在

2.2.4 其他常用命令

  • LPUSH / RPUSH — 向列表头部/尾部插入元素
  • LRANGE key start stop — 获取列表指定范围元素
  • SADD key member — 向集合添加成员
  • SMEMBERS key — 获取集合所有成员
  • ZADD key score member — 向有序集合添加成员
  • ZRANGE key start stop [WITHSCORES] — 按排名获取有序集合元素
契约校验高频命令组合:
WATCH key + MULTI / EXEC 用于乐观锁版本校验;
EVAL "script" numkeys key [key ...] [arg ...] 用于 Lua 原子化版本比对与更新。

2.3 键空间设计原则

键空间设计直接影响缓存命中率、可维护性与契约校验的复杂度。以下 5 条原则是业界最佳实践。

  1. 命名规范: 使用 业务域:对象类型:标识符[:子域] 的分层命名法。例如 user:profile:1001、order:detail:ORD20250301。避免无意义的前缀或纯数字 key。
  2. 控制 key 长度: key 本身占用内存,过长会浪费空间且降低查询效率。建议 key 总长度不超过 128 字节,但也要保证可读性。
  3. 避免热点 key: 单个 key 承载过高 QPS 会导致 Redis 实例 CPU 飙升。可通过 key 打散(如加随机后缀)或 本地缓存 缓解。
  4. 设置合理的 TTL: 所有缓存 key 原则上都应设置过期时间。TTL 应与数据变更频率匹配,避免“永久缓存”导致的数据一致性问题。
  5. 版本字段预留: 在契约校验场景中,每个缓存对象应预留版本字段(如 _version 或 _etag),便于后续实现乐观锁或版本比对。
避坑提醒: 不要将版本号直接拼入 key 名称(如 user:1001:v2),这会导致缓存击穿时大量 key 同时失效。正确的做法是将版本号作为 Hash 的 field 或 String 值的一部分,通过原子操作更新。

2.4 键空间设计对比:好设计 vs 坏设计

维度 坏设计 好设计
命名 u1001、order_20250301 user:info:1001、order:detail:20250301
长度 user_profile_cache_for_user_id_1001_in_system_a user:profile:1001
版本处理 user:1001:v2(key 内嵌版本) user:1001(Hash 内 field _version)
TTL 策略 所有 key 统一 3600 秒 按数据热度分层:热数据 300 秒,温数据 1800 秒
热点处理 单 key 承载所有请求 key 打散 + 本地缓存兜底

2.5 数据结构选择与契约校验的关系

下图展示了不同数据结构在契约版本校验中的典型定位,帮助你在设计阶段快速做出选择。

数据结构与契约校验关系图 数据结构与契约校验场景映射 契约校验引擎 String 版本化 JSON 载荷 ETag / 时间戳 Hash 字段级版本标记 部分更新校验 Sorted Set 版本号作为 score 范围版本比对 说明:String 适合完整对象版本校验;Hash 适合字段级细粒度版本控制;Sorted Set 适合版本排序与范围查询。 List 和 Set 在契约校验中较少直接使用,通常作为辅助数据结构。

2.6 实战:键空间设计示例

假设我们要为电商系统的商品详情页设计缓存,结合契约版本校验,推荐如下键空间方案:

# 商品基本信息(Hash 结构,字段级版本)
product:basic:1001
  field: name        -> "Redis 实战"
  field: price       -> "59.00"
  field: _version    -> "2025030101"

# 商品详情 JSON(String 结构,整体版本)
product:detail:1001
  value: {"title":"Redis 实战","author":"张三","_version":"2025030101"}

# 商品排行榜(Sorted Set,版本号作为 score)
product:ranking:category:books
  member: 1001  score: 2025030101
  member: 1002  score: 2025030102
设计要点: 每个缓存对象都显式包含 _version 字段,版本号使用 yyyyMMddHHmm 格式的时间戳或单调递增的序列号。这样在契约校验时,只需比对版本号即可判断缓存是否过期,无需反序列化整个对象。