Redis 基础回顾:核心数据结构与运行机制
在深入异步任务缓存一致性方案之前,必须重新夯实 Redis 的基础知识。本章从数据结构、过期策略、持久化机制和单线程模型四个维度展开,为后续实战章节建立共同的技术语言。
2.1 五种核心数据结构
Redis 提供了丰富的数据结构,每种结构都有其适用的缓存场景。下表总结了最常用的五种结构及其典型用途:
| 数据结构 | 底层编码 | 时间复杂度(关键操作) | 典型缓存场景 |
|---|---|---|---|
| String | int / embstr / raw | GET/SET: O(1) | 会话 Token、计数器、分布式锁 |
| Hash | ziplist / hashtable | HGET/HSET: O(1) | 对象缓存(用户信息、商品详情) |
| List | quicklist | LPUSH/RPOP: O(1) | 消息队列、时间线、异步任务队列 |
| Set | intset / hashtable | SADD/SISMEMBER: O(1) | 标签系统、去重集合、好友关系 |
| Sorted Set | ziplist / skiplist | ZADD: O(log N),ZRANGE: O(log N+M) | 排行榜、延迟队列、优先级任务 |
实战提示:在异步任务缓存一致性场景中,List 常用于任务队列(LPUSH 生产,BRPOP 消费),Sorted Set 用于延迟任务(以时间戳为 score),Hash 用于存储任务状态和重试次数。
2.2 过期策略与内存淘汰
Redis 的过期策略决定了缓存数据何时被自动删除,这对缓存一致性至关重要。过期策略分为两种:
- 被动过期(惰性删除):当客户端访问一个 key 时,检查其是否过期,若过期则删除并返回 nil。这种策略节省 CPU,但可能导致过期 key 长期占用内存。
- 主动过期(定期删除):Redis 每隔 100ms 随机抽取一批设置了过期时间的 key,检查并删除已过期的 key。默认每秒执行 10 次,每次最多扫描 20 个 key。
当内存达到 maxmemory 限制时,Redis 会触发内存淘汰策略。以下是 8 种淘汰策略的对比:
| 策略名称 | 作用范围 | 淘汰逻辑 | 适用场景 |
|---|---|---|---|
| noeviction | 所有 key | 不淘汰,写入直接报错 | 不允许丢失数据的缓存 |
| allkeys-lru | 所有 key | 淘汰最近最少使用的 key | 通用缓存,访问模式较均匀 |
| volatile-lru | 设置了 TTL 的 key | 淘汰最近最少使用的 key | 有时效性的缓存 |
| allkeys-random | 所有 key | 随机淘汰 | 缓存命中率不敏感的场景 |
| volatile-random | 设置了 TTL 的 key | 随机淘汰 | 有时效性且访问均匀 |
| volatile-ttl | 设置了 TTL 的 key | 淘汰剩余 TTL 最短的 key | 优先保留即将过期的数据 |
| allkeys-lfu | 所有 key | 淘汰最不经常使用的 key | 访问频率差异大的缓存 |
| volatile-lfu | 设置了 TTL 的 key | 淘汰最不经常使用的 key | 有时效性且频率差异大 |
避坑指南:在异步任务缓存一致性方案中,不要依赖过期策略来保证一致性。过期删除是最终一致性的兜底手段,而非主动同步机制。如果业务要求秒级一致性,必须使用主动失效或 binlog 监听。
2.3 持久化机制:RDB 与 AOF
Redis 的持久化机制决定了缓存数据在重启后能否恢复。理解 RDB 和 AOF 的差异,有助于设计缓存预热和故障恢复策略。
RDB(Redis Database)
- 原理:通过 fork 子进程,将内存数据全量快照写入磁盘。
- 触发方式:手动(SAVE/BGSAVE)、自动(配置 save 规则)。
- 优点:文件紧凑,恢复速度快,适合备份和灾难恢复。
- 缺点:可能丢失最后一次快照之后的数据;fork 阻塞主线程(大实例时明显)。
AOF(Append Only File)
- 原理:将每次写操作以协议格式追加到 AOF 文件末尾。
- 刷盘策略:always(每次写入 fsync)、everysec(每秒 fsync)、no(由操作系统决定)。
- 优点:数据安全性高(最多丢失 1 秒数据);文件可读,支持重写压缩。
- 缺点:文件体积通常大于 RDB;恢复速度慢于 RDB。
生产建议:同时开启 RDB 和 AOF。RDB 用于快速恢复和备份,AOF 用于保证数据完整性。在缓存一致性场景中,AOF 的 everysec 策略是平衡性能和安全的最佳选择。
2.4 单线程模型与 I/O 多路复用
Redis 的核心处理线程是单线程的,这意味着所有命令在同一个线程中顺序执行。理解单线程模型对设计缓存操作至关重要。
单线程的优势
- 无锁竞争:所有操作串行执行,不需要考虑并发锁问题,避免了上下文切换和锁竞争的开销。
- 原子性保证:单个命令天然是原子的,不需要额外加锁。多个命令的原子性需要通过 Lua 脚本或事务(MULTI/EXEC)实现。
- 可预测的性能:没有线程调度波动,延迟稳定,适合对延迟敏感的场景。
单线程的瓶颈
- CPU 密集型操作:如大 key 的遍历(KEYS、SMEMBERS)、复杂计算(SORT、ZUNIONSTORE)会阻塞其他命令。
- 慢查询:单个命令执行时间过长(如
HGETALL一个包含百万字段的 Hash),会导致后续命令排队等待。
I/O 多路复用机制
Redis 使用基于 epoll(Linux)/ kqueue(macOS)的 I/O 多路复用技术,单线程可以同时处理大量客户端连接。事件循环模型如下:
- 注册文件描述符(socket)到事件循环中。
- 当有新的请求到达时,事件循环将请求放入队列。
- 单线程从队列中取出请求,解析命令,执行操作,返回结果。
- 如果执行过程中需要读写磁盘(如 RDB 快照),则 fork 子进程处理,不阻塞主线程。
实战启示:在异步任务缓存一致性方案中,利用单线程的原子性特性,可以使用
SETNX 实现分布式锁,使用 Lua 脚本 保证多个缓存操作(如检查-删除-更新)的原子性。避免在 Redis 中执行耗时操作,以免影响任务队列的消费速度。
2.5 本章小结
Redis 的基础机制直接决定了缓存一致性方案的设计边界:
- 数据结构选择影响任务队列和状态存储的效率。
- 过期策略是最终一致性的兜底手段,但不能替代主动同步。
- 持久化机制决定了缓存重启后的恢复能力,AOF everysec 是推荐配置。
- 单线程模型要求我们避免慢查询,善用原子操作和 Lua 脚本。