3、Redis基础回顾:Redis数据结构、String与Hash在Token存储中的应用、过期策略

3.1 Redis核心数据结构概览

Redis 提供了多种高性能数据结构,在 Token 鉴权场景中,最常使用的是 String 和 Hash。理解它们的特点与适用边界,是构建高效缓存链路的基础。

数据结构 底层编码 单 Key 容量 典型操作 Token 场景适用性
String int / embstr / raw 512 MB SET / GET / SETEX / TTL 单 Token 字符串存储,简单直接
Hash ziplist / hashtable 2^32 - 1 个 field HSET / HGET / HDEL / HGETALL Token 元数据(uid + 过期 + 权限)结构化存储
List quicklist 2^32 - 1 个元素 LPUSH / RPOP / LRANGE 不适用于 Token 鉴权
Set intset / hashtable 2^32 - 1 个成员 SADD / SISMEMBER / SMEMBERS 黑名单 / 白名单集合
Sorted Set ziplist / skiplist 2^32 - 1 个成员 ZADD / ZRANK / ZREMRANGEBYSCORE Token 按时间排序淘汰
选型原则: 如果只需要存储一个 Token 字符串(如 JWT),String 是最优解;如果需要同时存储 Token 对应的用户 ID、角色、过期时间等多个字段,Hash 能减少 Key 数量,提升内存利用率。

3.2 String 在 Token 存储中的应用

String 是 Redis 最基础的数据结构,在 Token 鉴权中通常以 token:{uuid} 为 Key,以 JWT 或 Session ID 为 Value。配合 SETEX 命令,可以在写入时直接设置过期时间,原子性完成存储与过期绑定。

# 存储 Token,有效期 3600 秒
SETEX token:a1b2c3d4 3600 "eyJhbGciOiJIUzI1NiIs..."

# 读取 Token
GET token:a1b2c3d4

# 检查剩余生存时间
TTL token:a1b2c3d4

# 删除 Token(登出时使用)
DEL token:a1b2c3d4
性能数据: String 的 SET/GET 操作在单节点 Redis 上可达 10 万+ QPS,内存开销仅为 Key + Value + 元数据(约 50 字节)。适合高并发、低延迟的 Token 校验场景。

3.3 Hash 在 Token 存储中的应用

当 Token 需要携带多个属性(如 uid、role、expireAt、refreshToken)时,使用 Hash 可以将这些字段聚合在一个 Key 下,避免生成多个 String Key,减少内存碎片和网络往返。

# 存储 Token 元数据
HSET token:a1b2c3d4 uid 10086 role admin expireAt 1718000000 refreshToken "rt_xxxx"

# 读取单个字段
HGET token:a1b2c3d4 uid

# 读取所有字段
HGETALL token:a1b2c3d4

# 删除某个字段
HDEL token:a1b2c3d4 refreshToken

# 设置整个 Hash 的过期时间(需借助 Key 过期)
EXPIRE token:a1b2c3d4 3600
注意: Redis Hash 本身不支持 field 级别的 TTL,只能对整个 Key 设置过期时间。如果需要对单个 field 设置不同过期时间,需要拆分为多个 String Key 或使用 Sorted Set 辅助实现。

3.4 String vs Hash:Token 存储对比

对比维度 String Hash
Key 数量 每个 Token 属性一个 Key 一个 Key 包含多个 field
内存效率 小 Value 时略高 多字段时节省 Key 元数据开销
读写粒度 整个 Value 读写 支持单个 field 读写
过期控制 每个 Key 独立 TTL 整个 Key 统一 TTL
序列化开销 无(直接存字符串) 无(field/value 均为字符串)
适用场景 纯 Token 字符串,无元数据 Token + 用户信息 + 权限等结构化数据

3.5 过期策略:被动与主动

Redis 的过期策略是 Token 自动失效的基石。理解其机制,可以避免因过期 Key 堆积导致的内存泄漏或鉴权异常。

  • 被动过期(惰性删除): 当客户端访问一个 Key 时,Redis 检查其是否过期,若过期则删除并返回 nil。这种策略对 CPU 友好,但过期 Key 可能长时间占用内存。
  • 主动过期(定期删除): Redis 每隔 100ms 随机抽取一批设置了 TTL 的 Key,检查并删除其中过期的 Key。默认每次抽取 20 个,若过期比例超过 25%,则继续抽取。
生产建议: 不要依赖 Redis 过期策略做精确的定时清理。对于 Token 场景,建议在业务层配合 TTL 检查 和 定时任务补偿删除,确保过期 Token 不会在缓存中残留过久。

3.6 Token 过期策略选型

策略 实现方式 优点 缺点 适用场景
固定 TTL SETEX / EXPIRE 简单,Redis 原生支持 所有 Token 同时过期,可能引发缓存雪崩 短期 Token(如 30 分钟)
滑动 TTL 每次访问时重新 SETEX 活跃 Token 自动续期 写操作增加,需注意并发覆盖 用户在线状态保持
双 TTL + 刷新 Access Token 短 TTL + Refresh Token 长 TTL 安全与体验平衡 需要额外维护 Refresh Token 逻辑 OAuth2 / JWT 标准方案
定时批量删除 SCAN + DEL 扫描过期 Key 精确控制内存释放 增加 Redis 负载,需错峰执行 高安全合规场景

3.7 数据结构与过期策略联动架构

下图展示了 Token 从生成到过期淘汰的完整链路,以及 String / Hash 在其中的位置。

Token 存储与过期联动架构 客户端 应用服务 Redis String token:uuid → JWT Redis Hash token:uuid → {uid,role} 过期策略引擎 被动过期(惰性删除) 主动过期(定期删除) TTL 检查 + 定时补偿 说明: 1. 客户端请求经应用服务,根据 Token 类型选择 String 或 Hash 存储。 2. 过期策略引擎同时作用于 String 和 Hash,通过被动+主动方式清理过期 Key。 3. 生产环境建议配合业务层 TTL 检查,避免过期 Key 残留。

3.8 生产实践:Token 存储选型决策树

在实际项目中,可以根据以下决策树快速选择合适的数据结构:

  1. 是否只需要存储 Token 字符串本身?
    • 是 → 使用 String + SETEX
    • 否 → 进入下一步
  2. 是否需要存储多个字段(uid、role、expireAt 等)?
    • 是 → 使用 Hash + EXPIRE
    • 否 → 使用 String
  3. 是否需要 field 级别独立过期?
    • 是 → 拆分为多个 String Key,或使用 Sorted Set 辅助
    • 否 → 保持 Hash 统一过期
  4. 是否需要频繁更新部分字段?
    • 是 → Hash 的 HINCRBY / HSET 更高效
    • 否 → String 全量覆盖即可
最佳实践: 对于大多数 Token 鉴权场景,推荐使用 Hash 存储 Token 元数据,配合 滑动 TTL 策略。这样既能减少 Key 数量,又能灵活管理 Token 生命周期。如果 Token 本身是自包含的 JWT(无需额外元数据),则 String 是更轻量的选择。