2. Redis 缓存基础回顾:核心数据结构、过期策略、内存淘汰机制

在深入异步导出任务的降级与补偿设计之前,必须夯实 Redis 缓存的基础知识。本章将系统回顾 Redis 的五大核心数据结构、键的过期策略以及内存淘汰机制,为后续章节中缓存失效、数据一致性、资源隔离等设计决策提供理论支撑。

2.1 五大核心数据结构

Redis 之所以能成为缓存领域的首选,很大程度上归功于其丰富且高效的数据结构。每种结构都对应特定的应用场景,理解它们的底层实现与适用边界,是设计缓存方案的第一步。

数据结构 底层编码 典型命令 核心应用场景
String int / embstr / raw SET / GET / INCR / DECR 计数器、分布式锁、Session 共享、对象 JSON 缓存
Hash ziplist / hashtable HSET / HGET / HGETALL / HINCRBY 对象字段缓存(如用户信息)、短结构化数据
List quicklist LPUSH / RPUSH / LPOP / BRPOP 消息队列、时间线、最新消息列表
Set intset / hashtable SADD / SMEMBERS / SINTER / SUNION 标签系统、好友关系、去重统计
Sorted Set ziplist / skiplist + hashtable ZADD / ZRANGE / ZREVRANGE / ZSCORE 排行榜、延时队列、优先级任务调度
设计提示: 在异步导出任务中,Sorted Set 常用于构建延时队列(以时间戳作为 score),String 用于存储任务状态与进度,Hash 则适合缓存导出任务的元数据(如文件名、行数、创建时间)。选择合适的数据结构能显著降低代码复杂度。

下图展示了五种核心数据结构在内存中的逻辑形态与典型操作路径:

Redis 五大核心数据结构逻辑视图 String key → value SET/GET/INCR Hash key → field → value HSET/HGET/HGETALL List key → [e1, e2, ...] LPUSH/RPUSH/LPOP Set key → {e1, e2, ...} SADD/SMEMBERS/SINTER Sorted Set key → {e: score} ZADD/ZRANGE/ZREVRANGE 数据结构选择指南 • String:简单 KV、计数器、分布式锁 • Hash:对象字段级缓存,节省内存 • List:消息队列、时间线、流式数据 • Set:去重、交集/并集运算、标签 • Sorted Set:排行榜、延时队列 异步导出场景推荐 1. 任务队列 → Sorted Set (score=时间戳) 2. 任务状态 → String (status=progress) 3. 任务元数据 → Hash (field=文件名/行数) 4. 失败重试 → List (LPUSH+BRPOP) 5. 去重校验 → Set (SISMEMBER)

2.2 过期策略:TTL 与惰性删除

Redis 允许为每个键设置生存时间(TTL),到期后自动删除。这一机制是缓存失效的核心驱动力。Redis 采用 定期删除 + 惰性删除 的组合策略来平衡 CPU 与内存开销。

  • 定期删除(Active Expiry): Redis 每隔 100ms 随机抽取一部分设置了过期时间的 key,检查是否过期,若过期则删除。该策略避免了全量扫描带来的性能抖动。
  • 惰性删除(Lazy Expiry): 当客户端访问一个 key 时,Redis 会先检查该 key 是否已过期,若过期则立即删除并返回 nil。该策略确保了过期 key 不会被返回给客户端。
关键结论: 定期删除负责“主动清理”,惰性删除负责“兜底清理”。两者配合,既保证了过期 key 不会长期占用内存,又避免了定时扫描对 CPU 的冲击。在异步导出任务中,合理设置 TTL 可以自动清理过期任务记录,减少人工干预。

下图展示了过期策略的执行流程与交互关系:

Redis 过期策略执行流程 定时器 (100ms) 定期扫描过期 key 定期删除 随机抽样 + 删除 释放内存 客户端访问 key GET / HGET / ZRANGE 惰性删除 策略对比 • 定期删除:主动、定时、随机 优点:及时释放过期内存 缺点:可能漏删部分 key • 惰性删除:被动、按需、精准 优点:不浪费 CPU 缺点:过期 key 可能残留 • 两者互补,缺一不可

2.3 内存淘汰机制:当内存不足时

当 Redis 内存使用量达到 maxmemory 限制时,会触发内存淘汰策略。这一机制决定了哪些 key 会被优先删除以释放空间。理解不同淘汰策略的行为,对于设计缓存失效后的降级逻辑至关重要。

淘汰策略 淘汰范围 淘汰依据 适用场景
noeviction 不淘汰 直接返回 OOM 错误 不允许丢失任何数据的场景(如支付状态)
allkeys-lru 所有 key 最近最少使用 通用缓存,优先保留热点数据
volatile-lru 设置了 TTL 的 key 最近最少使用 仅淘汰可过期数据,保护持久化 key
allkeys-random 所有 key 随机 数据访问频率均匀,无热点
volatile-random 设置了 TTL 的 key 随机 对淘汰顺序无要求,仅淘汰可过期数据
volatile-ttl 设置了 TTL 的 key 剩余 TTL 最短 优先淘汰即将过期的数据
volatile-lfu (4.0+) 设置了 TTL 的 key 最不经常使用 淘汰访问频率最低的可过期数据
allkeys-lfu (4.0+) 所有 key 最不经常使用 淘汰全局访问频率最低的数据
避坑指南: 在异步导出任务中,如果使用 noeviction 策略,当内存写满时所有写入操作都会失败,可能导致任务状态无法更新、队列无法入队。建议使用 allkeys-lru 或 volatile-lru,并配合监控告警,确保缓存失效时任务系统能优雅降级。

2.4 过期与淘汰的协同关系

过期策略与内存淘汰机制并非独立运行,它们共同决定了 Redis 中数据的生命周期。理解两者的协同关系,有助于设计更健壮的缓存失效补偿方案。

  • 优先级: 过期删除的优先级高于内存淘汰。Redis 会先尝试通过定期删除和惰性删除释放空间,若仍不足,再触发内存淘汰。
  • 触发条件: 过期删除由 TTL 驱动,内存淘汰由 maxmemory 阈值驱动。两者可能同时发生,但互不干扰。
  • 数据流向: 一个 key 可能先被过期删除,也可能先被内存淘汰。对于设置了 TTL 的 key,过期删除是“主动退休”,内存淘汰是“被动清退”。
  • 设计建议: 在异步导出任务中,建议为所有缓存 key 设置合理的 TTL,并选择 volatile-lru 策略。这样既能保证热点数据优先保留,又能确保非热点数据在内存不足时被淘汰,同时持久化 key(如任务配置)不受影响。