2. Redis 基础回顾:Redis 数据结构详解

在深入削峰与缓存一致性实战之前,必须牢固掌握 Redis 的五种核心数据结构。本章将逐一剖析 String、List、Hash、Set、Sorted Set 的底层实现、适用场景、常用命令及其时间复杂度,为后续的架构设计打下坚实基础。

2.1 数据结构全景概览

Redis 的所有数据结构都以 Key-Value 形式存储,但 Value 的类型决定了操作能力与性能特征。下表对比了五种数据结构的核心差异:

数据结构 底层编码(3.2+) 最大元素数 有序性 唯一性 典型应用
String int / embstr / raw 512 MB — — 缓存、计数器、分布式锁
List quicklist 2³² - 1 插入顺序 否 消息队列、时间线、栈
Hash ziplist / hashtable 2³² - 1 — 字段唯一 对象存储、用户属性
Set intset / hashtable 2³² - 1 否 是 去重、标签、关注关系
Sorted Set ziplist / skiplist 2³² - 1 Score 排序 成员唯一 排行榜、延迟队列、限流
编码转换提示: 当 Hash 或 Sorted Set 的元素数量较少且值较小时,Redis 使用 ziplist 以节省内存;当超过阈值(默认 512 个或 64 字节)时,自动升级为 hashtable 或 skiplist。理解这一机制有助于预估内存与性能拐点。

2.2 String — 最基础的原子载体

String 是 Redis 最基础的数据结构,可以存储字符串、整数或浮点数。其底层采用 SDS(Simple Dynamic String),具备 O(1) 长度获取、预分配空间、二进制安全等特性。

常用命令与时间复杂度

命令 功能 时间复杂度 备注
SET key value 设置键值 O(1) 可附加 NX/XX/EX/PX
GET key 获取值 O(1) —
INCR key 原子自增 1 O(1) 用于计数器、限流
MSET k1 v1 k2 v2 批量设置 O(N) N 为键数量
GETSET key value 设置并返回旧值 O(1) 用于轮换 token
# 实战:分布式锁 + 计数器
> SET lock:order 1 NX EX 10
OK
> INCR order:count
(integer) 1001
> GET order:count
"1001"

2.3 List — 双向链表与消息队列

List 基于 quicklist(压缩链表 + 双向链表混合)实现,支持从两端高效插入/弹出。在削峰场景中,List 常作为 异步缓冲队列 使用。

常用命令与时间复杂度

命令 功能 时间复杂度 备注
LPUSH key value 左端插入 O(1) —
RPOP key 右端弹出 O(1) 实现 FIFO 队列
BLPOP key timeout 阻塞左弹出 O(1) 实现消费者等待
LRANGE key start stop 范围获取 O(S+N) S 为偏移量,N 为返回数
LLEN key 获取长度 O(1) —
避坑: LRANGE 在列表很长时(如 10 万+)会阻塞 Redis 主线程。生产环境应避免对大 List 执行全量范围查询,建议使用 LTRIM 定期裁剪或改用 Stream。

2.4 Hash — 对象级字段存储

Hash 适合存储结构化对象(如用户信息、商品详情),支持对单个字段进行原子读写,避免序列化整个对象的开销。

常用命令与时间复杂度

命令 功能 时间复杂度 备注
HSET key field value 设置字段 O(1) —
HGET key field 获取字段 O(1) —
HMSET key f1 v1 f2 v2 批量设置 O(N) N 为字段数
HGETALL key 获取所有字段与值 O(N) N 为字段数,大 Hash 慎用
HINCRBY key field inc 字段原子增减 O(1) 用于计数类字段
# 实战:用户积分字段原子更新
> HSET user:1001 score 500
(integer) 1
> HINCRBY user:1001 score 50
(integer) 550
> HGET user:1001 score
"550"

2.5 Set — 无序唯一集合

Set 基于 hashtable 或 intset 实现,支持高效的集合运算(交集、并集、差集),适用于标签系统、好友关系、去重场景。

常用命令与时间复杂度

命令 功能 时间复杂度 备注
SADD key member 添加成员 O(1) —
SISMEMBER key member 判断是否存在 O(1) —
SMEMBERS key 获取所有成员 O(N) N 为集合大小,大集合慎用
SINTER key1 key2 交集 O(N*M) N 为最小集合大小
SPOP key count 随机弹出 O(1) 用于抽奖、随机选取

2.6 Sorted Set — 有序排行榜核心

Sorted Set 是 Redis 最强大的数据结构之一,每个成员关联一个 score,通过 skiplist + hashtable 实现 O(log N) 的插入、删除与范围查询。在削峰场景中,它常用于 滑动窗口限流 与 延迟队列。

常用命令与时间复杂度

命令 功能 时间复杂度 备注
ZADD key score member 添加/更新成员 O(log N) N 为有序集合大小
ZRANGE key start stop 按排名范围获取 O(log N + M) M 为返回数
ZREVRANGE key start stop 按排名倒序获取 O(log N + M) 排行榜常用
ZRANK key member 获取成员排名 O(log N) —
ZREMRANGEBYSCORE key min max 按分数范围删除 O(log N + M) 用于滑动窗口清理
# 实战:滑动窗口限流(1 分钟窗口,最多 100 次)
> ZADD rate:limit:user001 1700000000 "req1"
> ZREMRANGEBYSCORE rate:limit:user001 0 1699999399
(integer) 0
> ZCARD rate:limit:user001
(integer) 1

2.7 数据结构选择决策流程

下图展示了在业务场景中如何快速选择合适的数据结构:

Redis 数据结构选择决策树 业务需求 需要排序? 需要唯一? 结构化字段? Sorted Set Set Hash 需要队列? List String Stream 决策要点: • 需要排序 + 唯一 → Sorted Set;仅唯一 → Set;结构化字段 → Hash • 需要队列 → List(简单)或 Stream(可靠);简单缓存/计数 → String

2.8 时间复杂度速查表

以下速查表汇总了各数据结构关键操作的时间复杂度,方便在架构设计时快速评估性能风险:

操作类型 String List Hash Set Sorted Set
单元素写入 O(1) O(1) O(1) O(1) O(log N)
单元素读取 O(1) O(1) O(1) O(1) O(log N)
批量写入 O(N) O(N) O(N) O(N) O(N log N)
范围查询 — O(S+N) O(N) — O(log N + M)
集合运算 — — — O(N*M) O(N+M)
架构师视角: 在削峰场景中,Sorted Set 的 O(log N) 写入是可控的,但 List 的 O(1) 两端操作更适合高吞吐的瞬时缓冲。对于需要去重与排序的限流场景,Sorted Set 是唯一选择;对于纯队列缓冲,List 或 Stream 更优。