Skip to content
All notes

Redis

归纳 Redis 数据类型、缓存应用与常见运行边界。

Redis

Redis 把数据放在内存中,支持多种数据类型和 TTL,常用于缓存、共享会话、计数、限流、排行榜和任务状态。

image.png image.png

数据类型与选择

类型用途常用命令
String缓存、计数器、锁;可存二进制数据SET、GET、INCR
List有序且可重复的列表、简单队列RPUSH、LPOP、LRANGE、LTRIM
Hash对象的字段和值HSET、HGET、HINCRBY
Set无序去重、交并差集SADD、SISMEMBER、SINTER
ZSet成员唯一,按 score 排序;排行榜、延迟队列ZADD、ZRANGE、ZINCRBY
Bitmap基于 String 的位操作,适合签到等布尔状态SETBIT、GETBIT、BITCOUNT
HyperLogLog近似去重计数,例如 UVPFADD、PFCOUNT
GEO地理位置和距离查询GEOADD、GEOSEARCH
Stream消息流、消费组、待确认消息管理XADD、XREADGROUP、XACK

Agent 的短期对话缓存可以用 List:按顺序追加,LRANGE 读取最近消息,LTRIM 控制窗口,再用 TTL 过期。需要按消息 ID 查属性时考虑 Hash;需要消费组、确认和重试管理时考虑 Stream。

image.png

Pub/Sub 与 Stream

Pub/Sub 把消息广播给在线订阅者,不保存消息,离线不能补收,适合即时通知。Stream 保存消息,支持按 ID 读取、消费组和 ACK,适合需要处理进度的消息消费;持久性仍取决于持久化、复制和裁剪配置。

SDS 与跳表

String 的字符串表示使用 SDS,保存长度和容量,获取长度为 O(1),能存包含 \0 的二进制数据,并通过预留空间减少扩容;整数等值还可能采用其他编码。

跳表在有序链表上增加随机层高的索引。查找时从高层跳跃,快要超过目标时降一层,平均查找、插入和删除复杂度为 O(log N)。例如查找 7,可以先从高层跳到 4,再降到底层经过 5 找到 7,无需从头遍历。

ZSet 的常见较大规模编码使用字典加跳表:字典按成员查分数,跳表支持排序和范围查询;小集合可使用紧凑编码。

持久化

方式恢复方式主要取舍
RDB加载某时刻的数据快照文件通常较小、恢复快;可能丢失快照后的变更
AOF重放写操作日志,可通过重写压缩丢失窗口取决于 fsync 策略;通常占用更多空间和恢复时间

可结合两者使用,具体取决于可接受的数据丢失窗口与恢复时间。

image.png

过期与内存淘汰

过期按 TTL 判断数据是否失效;淘汰是在达到 maxmemory 时释放空间,两者不是同一件事。

Redis 结合访问时检查的惰性过期,以及后台周期检查的主动过期。不会给每个 key 单独创建定时器,也不保证 TTL 到点就立即释放全部物理内存。

image.png

达到内存限制后,行为由淘汰策略决定:

  • noeviction:拒绝需要分配内存的写命令,读和删除通常仍可执行。
  • LRU:偏向淘汰最近较少访问的数据。
  • LFU:偏向淘汰访问频率较低的数据。
  • Random:随机淘汰。
  • volatile-ttl:从带过期时间的键中,优先淘汰剩余寿命短的键。

allkeys-* 面向所有键,volatile-* 只面向设置过期时间的键;LRU、LFU 使用近似策略。即使启用淘汰,若没有合适对象或仍无法释放足够空间,也可能拒绝写入。

image.png

事务

MULTI → 命令入队 → EXEC。入队期间其他客户端仍能执行命令;到 EXEC 执行队列时,其他客户端命令不会插入其中。Pipeline 只是减少网络往返,本身不提供事务隔离。

WATCH 在 MULTI 前监视 key;执行 EXEC 前,若受监视的键发生修改,事务取消并返回空结果,客户端决定是否重试。

Redis 不支持事务回滚:入队阶段的命令名、参数等错误会导致整个事务拒绝执行;执行阶段某条命令失败,其他命令仍会继续,已经成功的修改不会撤销。参见 Redis 事务文档。

image.png

分布式锁

用一条命令原子地设置持有者和过期时间:

SET lock_key request_id NX PX 30000

NX 表示不存在才写入,PX 设置毫秒 TTL,request_id 是本次持锁的唯一标识。释放时用 Lua 原子地比较标识并删除,避免误删别人的锁;续期也应检查持有者。

执行时间超过 TTL、进程暂停或主从切换,都可能破坏预期的互斥。严格的一致性需求还要由业务端校验,例如用 fencing token 拒绝过期持有者的写入。参见 Redis 分布式锁说明。

缓存穿透、击穿、雪崩

问题原因应对
穿透请求的数据在缓存和数据库中都不存在,持续回源校验参数、短 TTL 缓存空值、布隆过滤器拦截确定不存在的 key
击穿单个热点 key 失效,大量请求同时回源合并回源请求或互斥重建;能接受旧数据时,可用逻辑过期后异步刷新
雪崩大量 key 同时失效,或缓存服务不可用TTL 加随机偏移、提高可用性、限流和降级,必要时增加本地缓存

热点数据不设物理过期时间时,也需要更新与清理策略;后台刷新返回旧值的方案,要明确业务能接受多旧的数据。

image.png image.png