Redis 核心数据结构实战:五种类型用法与选型

为什么每个开发者都要懂 Redis 数据结构

Redis 核心数据结构 不只是「缓存的五种容器」,而是决定你代码简洁度与系统性能的关键。很多团队把 Redis 当成只会 GET/SET 的旁路缓存,结果要么用 String 硬塞 JSON 导致更新要整体序列化,要么用 List 当消息队列却丢了可靠性。本文逐一拆解 String、Hash、List、Set、ZSet 这五种核心结构的底层实现、高频命令与典型场景,并给出一张选型速查表,帮你真正「用对结构」而不是「只会赋值」。

理解每种子结构适合的读写模式,比记住命令本身更重要。下面每节都会回答三个问题:它底层是什么、最顺手的命令有哪些、什么场景该用它。

一、String:最朴素也最常用的键值

String 是 Redis 的基石,底层根据值自动选择 int、embstr 或 raw 编码。它不只是存字符串,还能存整数(直接计数)、二进制(位图)。常见用法包括计数器、分布式锁、短_token 与 BitMap 统计。

# 计数器:阅读量、点赞数,原子自增
INCR article:1001:views

# 分布式锁:不存在才设置,10 秒自动过期
SET lock:order:123 "1" NX EX 10

# 缓存 JSON(注意整体序列化,适合读多写少)
SET user:1 "{"name":"suzhe","age":30}" EX 3600

# BitMap:日活统计,每一位代表一个用户是否活跃
SETBIT user:active:20261027 10001 1
BITCOUNT user:active:20261027

用 INCR 做限流计数、用 SET NX EX 做锁都是经典姿势。但要警惕「大 key」:一个几 MB 的 String 会阻塞单线程,过期删除也可能引发延迟毛刺。

二、Hash:对象字段的最佳载体

Hash 把键值对聚合在一个 key 下,非常适合表达「一个对象的多个字段」。相比把整个对象序列化成 String,Hash 支持单字段读写与原子自增,更新一个字段不必整体重传。

# 存用户对象,字段级更新
HSET user:1 name suzhe age 30 vip 1
HGET user:1 name
HGETALL user:1
HINCRBY user:1 age 1          # 年龄 +1,原子操作

# 购物车:每个商品一个 field,value 是数量
HSET cart:1001 sku_123 2
HINCRBY cart:1001 sku_123 1   # 加购数量 +1

Hash 与「String 存 JSON」的取舍很关键:要频繁改个别字段、要单独读某个字段,优先 Hash;要整体读写、且客户端自己做序列化更方便,才用 JSON String。

维度Hash(字段级)String(JSON)
更新单个字段只改对应 field,省带宽需整体读改写写回
读取单个字段HGET 直接取需解析整个 JSON
客户端依赖服务端管结构客户端管序列化
适用对象、配置、购物车整体读写、跨语言简单缓存

三、List:有序可重复的序列

List 底层在元素少时用 listpack(旧称 ziplist)紧凑存储,元素多时转为 quicklist(链表 + 紧凑节点)。它是「有序且可重复」的序列,适合做最新列表、简单队列与栈。

# 最新文章列表:左进右出,保留最近 10 条
LPUSH news 1001 1002 1003
LTRIM news 0 9
LRANGE news 0 9          # 取最新 10 条

# 简单队列:生产者左推,消费者右弹
LPUSH queue:tasks "{"job":"resize"}"
RPOP queue:tasks

# 栈:同侧进出
LPUSH stack a b c
LPOP stack

轻量队列用 LPUSH+RPOP 足够,但它不像 Kafka 那样有消费组与可靠确认。如果要求「至少一次、可重放、多消费者」,应改用 Streams(本站已有 Redis Streams 专文)。List 更适合「最新 N 条」「简单 FIFO」这类场景。

四、Set:去重与集合运算

Set 是无序且唯一的集合,底层用哈希表(小数据时用 listpack 编码)。它最擅长去重与集合运算:共同好友、兴趣标签交集、抽奖去重都天然适配。

# 给用户打标签(自动去重)
SADD user:1:tags redis golang docker
SADD user:2:tags redis python

# 共同兴趣:交集
SINTER user:1:tags user:2:tags     # => redis

# 抽奖:随机抽取一名中奖者(不放回)
SPOP lottery:2026 1

# 判断某值是否在集合中(O(1))
SISMEMBER user:1:tags redis

与布隆过滤器不同,Set 是「精确去重」——结果 100% 准确,代价是内存随元素增长;布隆过滤器是概率去重,省内存但有误判。需要精确成员判定时用 Set,只需要「大概在不在」时用布隆过滤器。

五、ZSet(Sorted Set):带分数的有序集合

ZSet 是 Redis 里最强大的结构:每个成员带一个 score,底层用跳表(skiplist)+ 哈希实现,既能按分数排序,又能按成员 O(1) 取分。排行榜、延迟队列、优先级任务、范围统计都离不开它。

# 排行榜:分数即积分
ZADD leaderboard 100 suzhe 95 alice 88 bob
ZREVRANGE leaderboard 0 9 WITHSCORES   # Top 10(高到低)
ZRANK leaderboard suzhe                # 某人排名

# 延迟队列:score 设为执行时间戳
ZADD delay:queue 1761532800 "task:1"
# 消费者轮询:取到期任务
ZRANGEBYSCORE delay:queue 0 1761532800

# 范围统计:某时段内的点击数
ZCOUNT metrics:clicks 1000 2000

ZSet 的延迟队列思路很妙:把 score 设为「计划执行时间」,消费者用 ZRANGEBYSCORE 拉取已到期的任务即可。比 List 的「即时队列」多了「按时触发」能力,是轻量调度器的常用实现。

六、选型速查:什么时候用哪个

结构关键特征典型场景避免误用
String单值、可整数、可位图计数、分布式锁、token、BitMap别把大对象塞成超大 String
Hash字段级 KV对象、配置、购物车字段过多会变成大 key
List有序可重复最新列表、简单队列/栈别当可靠消息队列用
Set无序唯一去重、交集/差集、抽奖超大数据集注意内存
ZSet带分有序排行榜、延迟队列、优先级分数精度用整数避免浮点

七、内存与性能:大 key、编码与过期

选对结构只是第一步,生产里还Watch三项:大 key、编码优化与过期策略。大 key(如一个几 MB 的 Hash 或 List)会让单次命令阻塞、迁移变慢、集群数据倾斜。Redis 对小数据会自动用紧凑编码(listpack/intset),省内存也快,一旦超过阈值才转哈希表。

# 扫描全实例的大 key(生产用 --bigkeys 先做只读分析)
redis-cli --bigkeys

# 查看某个 key 的底层编码
DEBUG OBJECT user:1
# 输出含 encoding:hashtable / listpack / intset 等

# 主动过期(避免热点 key 同时失效造成缓存雪崩)
EXPIRE user:1 3600
# 错峰:给不同 key 加随机过期偏移

过期删除采用「惰性删除 + 定期抽样」:访问时才真正删,后台周期性抽查。这意味着大 key 过期时可能有一次删除阻塞,因此大 key 要拆分、热 key 要错峰过期。关于缓存模式与雪崩/穿透防护,可参考 Redis 缓存设计实战。

八、常见误区排障

误区现象修正
用 String 存大 JSON 又频繁改字段每次整体读写,带宽与 CPU 飙升改用 Hash 字段级更新
List 当可靠消息队列消费者崩溃丢消息、无消费组改用 Streams 或专业 MQ
ZSet 分数用浮点排序出现精度抖动用整数(如毫秒时间戳、放大 100 倍)
所有 key 同一过期时间缓存雪崩,同一刻全失效基础 TTL + 随机抖动
一个 Hash 存上万 field大 key 阻塞、迁移慢按业务拆分多个 key 或加前缀分片

九、总结:把结构选对比命令背熟更重要

Redis 核心数据结构 的真正价值,在于「按访问模式选容器」:单值计数与锁用 String,对象字段用 Hash,最新序列用 List,去重交集用 Set,排序排名用 ZSet。记住这张选型表,你就能在缓存、排行榜、队列、计数等场景里写出既省内存又高性能的代码,而不再只会 SET/GET 一把梭。把它和 Redis 缓存设计 的缓存模式、以及容器化部署(Docker)结合起来,配合 GitHub Actions 的压测与 VS Code 里的 Redis 插件,你的 Redis 实践会稳很多。

上一篇 bat 实战:带语法高亮的现代化 cat 替代品
下一篇 Docker 磁盘空间爆满排查:overlay2 清理实战
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]