Redis 五大数据类型

Redis 之所以强大,关键在于它不是"只能存字符串的缓存",而是内置多种数据结构,每种都配了一批专用命令。选对类型,经常能让一段复杂的业务代码退化成一两条 Redis 命令。本篇先把五大类型整体过一遍,后面四篇会逐一深入讲命令。

类型与"key-value"的关系

很多人第一次学 Redis 时会困惑:Redis 不是 key-value 吗,怎么又有"5 种类型"?其实这是两层概念:

所以更准确的说法是:Redis 是一个 "key - 数据结构" 存储——给定一个 key,你能拿到的不只是字符串,还可能是一个列表、一个哈希表、一个集合……Redis 还专门为每种数据结构做了高效实现(quicklist、ziplist、skiplist 等),让对应操作保持低复杂度。

除了五大基础类型,Redis 还有几个进阶类型:Bitmap(位图,基于 string,做签到统计)、HyperLogLog(基数估算,统计 UV)、Geo(地理坐标)、Stream(消息流,5.0 引入)。这些在进阶场景再讲。

类型一:string 字符串

string 是 Redis 最基础、最常用的类型——一个 key 对应一个值。值虽然是"字符串",但 Redis 是二进制安全的,意味着可以存任何二进制数据:一段文本、一个数字、一个序列化的 JSON、甚至一张图片(虽然不建议把大文件塞 Redis)。

# string:最基础类型,二进制安全(可存文本、数字、图片、JSON)
SET user:1 '{"name":"小明","age":20}'
GET user:1
# 数字可以原子自增
SET counter 0
INCR counter          # 1
INCRBY counter 100    # 101

string 的典型场景:缓存数据库查询结果(把 SQL 结果序列化成 JSON 存进去)、计数器(点赞数、浏览量,PV/UV)、分布式锁(SET key value NX EX)、限流(固定窗口计数)、存 HTML 片段做页面缓存。

类型二:hash 哈希

hash 适合存"对象"——一个 key 下有多个 field-value,就像一个 Redis 内置的小对象。比起把整个 JSON 序列化成 string 存,hash 的优势是可以单独读写某个字段,不必每次取出整个对象再写回。

# hash:一个 key 下多个 field-value,像一个小对象
HSET user:1 name "小明" age 20 city "北京"
HGET user:1 name      # "小明"
HGETALL user:1        # 列出所有字段
HMGET user:1 name age # 一次取多个字段

典型场景:用户信息(user:1user:2 各一个 hash)、商品详情、配置项。每改一个字段(比如用户改名)只需 HSET 那一个字段,O(1) 复杂度;而 string 方案要取出整个 JSON、解析、改字段、再写回,既慢又容易并发冲突。

类型三:list 列表

list 是有序、可重复的元素序列,在 Redis 早期版本里被实现成双向链表,3.2 之后用 quicklist(链表 + ziplist 的混合),元素较少时还会用 listpack 压缩。无论哪种实现,头尾插入弹出都是 O(1)。

# list:有序、可重复的双向链表
LPUSH queue "a" "b"   # 头插,现在是 [b, a]
RPUSH queue "c"       # 尾插,现在是 [b, a, c]
LRANGE queue 0 -1     # 取全部
LPOP queue            # 弹出 b

典型场景:简单消息队列(LPUSH 生产、RPOP 消费,先进先出)、最新 N 条记录(博客最新文章、最新动态)、BLPOP 阻塞等待做任务队列、有限集合(用 LTRIM 保留前 N 个)。

类型四:set 集合

set 是无序、不重复的元素集合。它的杀手锏是集合运算:交集、并集、差集——一条命令搞定关系数据库要 JOIN 半天的事。

# set:无序、不重复的集合
SADD tags "js" "css" "html"
SMEMBERS tags         # 所有成员
SISMEMBER tags "js"   # 1 表示存在
SCARD tags            # 成员数 3

典型场景:标签系统(文章标签、商品标签)、共同好友 / 共同关注(两个用户 set 求交)、去重(今日 UV、活跃用户)、抽奖(SRANDMEMBER 随机抽 N 个)、黑白名单。

类型五:sorted set 有序集合(zset)

zset 是 Redis 的"明星类型":它不重复且每个成员带一个 score(分数),自动按分数排序。底层用"跳表 + 字典"双重结构,既支持范围查询又支持单点查询,复杂度都是 O(log N)。

# sorted set (zset):带分数的集合,自动按分数排序
ZADD rank 100 "小明" 85 "小红" 92 "小刚"
ZRANGE rank 0 -1 WITHSCORES    # 升序: 小红 85, 小刚 92, 小明 100
ZREVRANGE rank 0 0 WITHSCORES  # 降序第一名: 小明 100

典型场景:排行榜(游戏积分榜、热词榜、热搜榜,这是 zset 的杀手锏)、延迟队列(score 存"执行时间戳",后台轮询取小于当前时间的)、分页(按分数范围取一段)、带权重的标签(权重做 score)。

类型选型对照表

+----------+----------+---------------------------------+----------------------+
|  类型    |  特征    |  典型用途                       |  关键命令            |
+----------+----------+---------------------------------+----------------------+
| string   | 单值     | 缓存、计数器、锁                | SET/GET/INCR        |
| hash     | 对象     | 用户/商品信息                   | HSET/HGET/HGETALL   |
| list     | 序列     | 队列、最近访问                  | LPUSH/RPOP/LRANGE   |
| set      | 集合     | 标签、共同好友                  | SADD/SINTER/SMEMBERS|
| zset     | 排序集合 | 排行榜、延迟队列                | ZADD/ZRANGE/ZINCRBY |
+----------+----------+---------------------------------+----------------------+
# 选型口诀:
# 缓存一个值 -> string
# 存一个对象 -> hash
# 排队/最新 N 条 -> list
# 不重复 + 集合运算 -> set
# 不重复 + 自动排序 -> zset

选型是 Redis 设计的核心问题。一个经验法则:先想清楚"我需要对这个数据做什么操作",再倒推类型。比如"经常改单个字段" -> hash,"需要按分数排名" -> zset,"需要求交集" -> set。选错了类型,后面再优化代码也救不回来。

类型不能"中途换"

有一个常见误区要注意:一个 key 的 value 类型一旦确定就不能变。如果你 SET 了一个 key 为 string,后面想 LPUSH 它当 list 用,Redis 会报错:

报错信息类似 WRONGTYPE Operation against a key holding the wrong kind of value。要换类型,必须先 DEL 删掉这个 key 再重新创建。命名规范上一般用前缀暗示类型(如 user: 是 hash、rank: 是 zset),避免团队协作时混淆。

小结

这一篇你了解了 Redis 的五大基础类型——string(单值/计数)、hash(对象)、list(队列)、set(去重/集合运算)、zset(排序)。后面四篇会逐个类型深入到具体命令,每个命令都给出可运行的示例。

← 上一篇 Redis 环境安装与 redis-cli

下一篇 Redis 字符串命令详解

✈️💬