目录
[一、String 字符串类](#一、String 字符串类)
[二、Hash 哈希类](#二、Hash 哈希类)
[三、List 列表类](#三、List 列表类)
[✅Redis 分频道阻塞消息队列模型](#✅Redis 分频道阻塞消息队列模型)
[四、Set 集合类](#四、Set 集合类)
[五、Zset 有序集合类](#五、Zset 有序集合类)
如前文所说,redis的五大数据类型是指的键(key)所对应的值(value)值的数据类型,分别是String、Hash、List、Set、Zset这五类,本文将介绍如何创建及使用这五种数据类型,并简单介绍其应用场景。
一、String 字符串类
字符串数据类型是redis中最基础的数据类型,需要注意的是:
- redis中所有的key都是字符串类型,并且其他4中数据类型也是在字符串的基础上构建的;(例如列表和集合的元素类型就是字符串。所以字符串类型也为我们学习其他4类数据类型提供支撑。)
- 字符串的一般表现形式有JSON、XML、数字(整型 / 浮点型)、二进制数据流、图片、视频等。
1.常用基础命令
常用命令分为三类,分别是常用操作命令 、计数器原子操作 和针对字符串操作命令如下表:
| 命令 | 执行效果 | 时间复杂度 |
|---|---|---|
set key value [key value...] |
设置 key 的值是 value | O(k),k 是键个数 |
get key |
获取 key 的值 | O(1) |
del key [key ...] |
删除指定的 key | O(k),k 是键个数 |
mset key value [key value...] |
批量设置指定的 key 和 value | O(k),k 是键个数 |
mget key [key ...] |
批量获取 key 的值 | O(k),k 是键个数 |
incr key |
指定的 key 的值 +1 | O(1) |
decr key |
指定的 key 的值 -1 | O(1) |
incrby key n |
指定的 key 的值 +n | O(1) |
decrby key n |
指定的 key 的值 -n | O(1) |
incrbyfloat key n |
指定的 key 的值 +n | O(1) |
append key value |
指定的 key 的值追加 value | O(1) |
strlen key |
获取指定 key 的值的长度 | O(1) |
setrange key offset value |
覆盖指定 key 的从 offset 开始的部分值 | O(n),n 是字符串长度,通常视为 O(1) |
getrange key start end |
获取指定 key 的从 start 到 end 的部分值 | O(n),n 是字符串长度,通常视为 O(1) |
2.经典应用场景
前面的博客中有讲过关于这五大类型的内部编码方式,对于String类,有三种编码方式,分别是 int (用来表示8个字节的长整型)、embstr (表示较短的字符串)、raw(表示较长的字符串)。根据不同的编码方式,其应用场景也有所区别。
✅缓存(cache)
我们都知道redis最大的优点就是快!因为它是直接从内存中读写数据,那么这个特点就很适合让redis拿来当缓存,以此可以加快访问数据的效率。
下图是比较典型的缓存使用场景,其中 Redis 作为缓冲层,MySQL 作为存储层,绝大部分请
求的数据都是从 Redis 中获取。

- 用户通过向业务层发送get请求,等待响应。
- 业务层收到请求后先在查看缓存层(redis)中有没有该数据
- 若有,则直接返回给业务层,业务层再将拿到的数据响应给用户。
- 若没有,则到存储层(mysql,硬盘中)读取数据,写到redis中(方便下次直接再缓存中拿)。数据再经过缓存层和业务层响应给用户。
✅计数功能(counter)
上面命令总结部分介绍了一类名为计数器原子操作的命令,就是为了避免在开发中遇到多线程并发造成 数据一致性问题 (多个线程抢占式地对同一个共享资源进行自增或自减操作,如果此时自增/自减不是原子性的,则会可能导致写覆盖的问题,最终导致数据不合法) 。
Redis 的 incr / incrby/ decr/ decrdy都是单命令原子操作,不需要额外加锁,就能安全做并发计数。
常被用来统计视频播放量、点赞/收藏量等。如下图的统计视频播放量的流程图:

✅共享会话(session)
如图所示,一个分布式 Web 服务将用户的 Session 信息(例如用户登录信息)保存在各自的服务器中,但这样会造成一个问题:出于负载均衡的考虑,分布式服务会将用户的访问请求均衡到不同的服务器上,并且通常无法保证用户每次请求都会被均衡到同一台服务器上,这样当用户刷新一次访问时可能会发现需要重新登录,这个问题是用户无法容忍的。你想我刚登录完,刷新一下又让我重新验证登录,烦死啦!

所以解决方案就是把 Session 统一存入 Redis,实现 Session 共享,不管请求落到哪一台 Web 服务器,都去 Redis 读取登录信息,就不会出现反复登录的情况。如下图:

二、Hash 哈希类
reids中的数据是以键值对的形式存储的,大家熟知的数据结构hash也是key-value的形式。那么存储在redis中的hash类型的数据就类似于 key (外层key) ---> (外层value){field1:value1,field2:value2,...}。
1.常用基础命令
对于hash类型的value,常用命令如下表:
| 命令 | 执行效果 | 时间复杂度 |
|---|---|---|
hset key field value |
设置值 | O(1) |
hget key field |
获取值 | O(1) |
hdel key field [field ...] |
删除 field | O(k),k 是 field 个数 |
hlen key |
计算 field 个数 | O(1) |
hgetall key |
获取所有的 field-value | O(k),k 是 field 个数 |
hmget key field [field ...] |
批量获取 field-value | O(k),k 是 field 个数 |
hmset key field value [field value ...] |
批量设置 field-value | O(k),k 是 field 个数 |
hexists key field |
判断 field 是否存在 | O(1) |
hkeys key |
获取所有的 field | O(k),k 是 field 个数 |
hvals key |
获取所有的 value | O(k),k 是 field 个数 |
hsetnx key field value |
设置值,但必须在 field 不存在时才能设置成功 | O(1) |
hincrby key field n |
对应 field-value +n | O(1) |
hincrbyfloat key field n |
对应 field-value +n | O(1) |
hstrlen key field |
计算 value 的字符串长度 | O(1) |
可以看到hash类型的数据也分为基本命令和原子操作命令。
2.经典应用场景
Hash的内部编码有两种,分别是ziplist (hash中元素较少/值较短) 和 hashtable(ziplist无法满足时)两种。其经典应用场景如下:
✅表示关系型数据表
| uid | name | age | city |
|---|---|---|---|
| 1 | James | 28 | Beijing |
| 2 | Johnathan | 30 | Xian |
例如上表,利用redis的hash类型存储该数据表就是这样的:

但对于一些有关联主键的表,表示表与表之间的关联关系就会将问题变得复杂很多。所以一般不使用redis来作为数据库存储关系型数据,因为我们有更好的选择,例如Mysql、Oracle、SQLite等。
三、List 列表类
List 列表类用于存储一个有序的字符串元素集合,底层采用双向链表或 quicklist 实现,支持从两端高效地插入和弹出元素,因此非常适合做消息队列、时间线、最新列表等场景。
1.常用基础命令
其常用命令按操作类型整理如下表:
| 命令 | 执行效果 | 时间复杂度 |
|---|---|---|
rpush key value [value ...] |
从列表右侧依次插入一个或多个元素 | O(k),k 是元素个数 |
lpush key value [value ...] |
从列表左侧依次插入一个或多个元素 | O(k),k 是元素个数 |
| **`linsert key before | after pivot value`** | 在指定元素 pivot 的前面或后面插入 value |
lrange key start end |
获取指定索引区间内的所有元素 | O(s+n),s 是 start 偏移量,n 是 start 到 end 的范围 |
lindex key index |
获取指定索引位置的元素 | O(n),n 是索引的偏移量 |
llen key |
计算列表的元素个数 | O(1) |
lpop key |
从列表左侧弹出一个元素 | O(1) |
rpop key |
从列表右侧弹出一个元素 | O(1) |
lrem key count value |
按 count 规则删除列表中值为 value 的元素 | O(k),k 是元素个数 |
ltrim key start end |
只保留指定索引区间内的元素,其余全部删除 | O(k),k 是元素个数 |
lset key index value |
把指定索引位置的元素修改为 value | O(n),n 是索引的偏移量 |
blpop / brpop |
阻塞式地从列表左 / 右侧弹出元素,列表为空时等待 | O(1) |
2.经典应用场景
✅消息队列
如图所示,list类 可以使用lpush + brpop 命令组合实现经典的阻塞式生产者 - 消费者模型队列。生产者客户端使用 lpush 从列表左侧插入元素,多个消费者客户端使用 brpop 命令阻塞式地从队列中 "争抢" 队尾元素。依靠多个消费者客户端,实现消费端的负载均衡与高可用。

✅Redis 分频道阻塞消息队列模型
Redis 同样使用 lpush + brpop 命令,但通过不同的键模拟频道的概念,不同的消费
者可以通过 brpop 不同的键值,实现订阅不同频道。例如微博博 Timeline,每个人有自己的微博列表,此处就可以考虑用List,因为list有序并支持范围搜索。

四、Set 集合类
Set 集合类用于存储一组无序且不重复的字符串元素,底层采用 hashtable 或 intset 实现,支持高效的成员判断与集合间的交、并、差运算,因此非常适合做去重、标签、共同好友等场景。
1.常用基础命令
其常用命令整理如下表:
| 命令 | 执行效果 | 时间复杂度 |
|---|---|---|
sadd key element [element ...] |
向集合中添加一个或多个元素 | O(k),k 是元素个数 |
srem key element [element ...] |
从集合中删除一个或多个元素 | O(k),k 是元素个数 |
scard key |
计算集合的元素个数 | O(1) |
sismember key element |
判断元素是否在集合中 | O(1) |
srandmember key [count] |
随机获取集合中指定个数的元素,元素不出集合 | O(n),n 是 count |
spop key [count] |
随机弹出集合中指定个数的元素,元素移出集合 | O(n),n 是 count |
smembers key |
获取集合中的所有元素 | O(k),k 是元素个数 |
sinter key [key ...] / sinterstore |
求多个集合的交集,sinterstore 会把结果存入目标集合 | O(m * k),k 是几个集合中元素最小的个数,m 是键个数 |
sunion key [key ...] / sunionstore |
求多个集合的并集,sunionstore 会把结果存入目标集合 | O(k),k 是多个集合的元素个数总和 |
sdiff key [key ...] / sdiffstore |
求多个集合的差集,sdiffstore 会把结果存入目标集合 | O(k),k 是多个集合的元素个数总和 |
2.经典应用场景
✅标签
Redis 集合类型很适合实现标签(tag)功能。
比如 A 用户感兴趣的标签是娱乐、体育,B 用户感兴趣的标签是历史、新闻。基于标签数据,我们可以筛选出拥有相同标签的用户,也能算出多个用户之间共同的标签,用来做用户兴趣分析。电商平台就经常利用这套标签体系,给不同兴趣标签的用户做个性化商品推荐。
下面的演示通过集合类型来实现标签的若干功能。
🔹给用户添加标签
bash
sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag2 tag3 tag5
...
sadd user:k:tags tag1 tag2 tag4
🔹给标签添加用户
bash
sadd tag1:users user:1 user:3
sadd tag2:users user:1 user:2 user:3
...
sadd tagk:users user:1 user:4 user:9 user:28
🔹删除用户下的标签
bash
srem user:1:tags tag1 tag5
...
🔹删除标签下的用户
bash
srem tag1:users user:1
srem tag5:users user:1
...
🔹计算用户的共同兴趣标签
bash
sinter user:1:tags user:2:tags
五、Zset 有序集合类
Zset 有序集合类用于存储一组不重复的字符串元素,并为每个元素关联一个 score 分值,底层采用 ziplist 或 skiplist + dict 实现,元素按 score 自动排序,因此非常适合做排行榜、延时队列、带权重的去重列表等场景。
1.常用基础命令
其常用命令整理如下表:
| 命令 | 执行效果 | 时间复杂度 |
|---|---|---|
zadd key score member [score member ...] |
向有序集合添加成员,并设置其 score 分值 | O(k * log(n)),k 是添加成员的个数,n 是当前有序集合的元素个数 |
zcard key |
计算有序集合的成员个数 | O(1) |
zscore key member |
获取指定成员的 score 分值 | O(1) |
zrank key member / zrevrank key member |
获取成员按 score 升序 / 降序的排名 | O(log(n)),n 是当前有序集合的元素个数 |
zrem key member [member ...] |
删除指定的成员 | O(k * log(n)),k 是删除成员的个数,n 是当前有序集合的元素个数 |
zincrby key increment member |
为指定成员的 score 分值加上 increment | O(log(n)),n 是当前有序集合的元素个数 |
zrange key start end [withscores] / zrevrange key start end [withscores] |
按排名区间升序 / 降序获取成员,可同时返回 score | O(k + log(n)),k 是获取成员的个数,n 是当前有序集合的元素个数 |
zrangebyscore key min max [withscores] / zrevrangebyscore key max min [withscores] |
按 score 区间升序 / 降序获取成员,可同时返回 score | O(k + log(n)),k 是获取成员的个数,n 是当前有序集合的元素个数 |
zcount key min max |
统计 score 在指定区间内的成员个数 | O(log(n)),n 是当前有序集合的元素个数 |
zremrangebyrank key start end |
删除指定排名区间内的成员 | O(k + log(n)),k 是获取成员的个数,n 是当前有序集合的元素个数 |
zremrangebyscore key min max |
删除 score 在指定区间内的成员 | O(k + log(n)),k 是获取成员的个数,n 是当前有序集合的元素个数 |
zinterstore destination numkeys key [key ...] |
对多个有序集合求交集,并将结果存入 destination | O(n * k) + O(m * log(m)),n 是输入的集合最小的元素个数,k 是集合个数,m 是目标集合元素个数 |
zunionstore destination numkeys key [key ...] |
对多个有序集合求并集,并将结果存入 destination | O(n) + O(m * log(m)),n 是输入集合总元素个数,m 是目标集合元素个数 |
2.经典应用场景
有序集合最典型的使用场景就是排行榜。像各类网站的热点榜单,排序维度十分丰富,可以按发布时间、阅读量、点赞数等进行排序。这里我们以点赞数量作为排序依据,来维护每日热榜。
✅排行榜
🔹增加点赞数
用户 james 发布文章拿到 3 个赞,使用zadd初始化分数;后续每多一个点赞,用zincrby增加分数:
zadd user:ranking:2022-03-15 3 james
zincrby user:ranking:2022-03-15 1 james
🔹移除榜单用户
遇到用户注销、刷赞作弊等场景,需要把用户从榜单剔除,使用zrem删除成员,示例移除 tom:
zrem user:ranking:2022-03-15 tom
🔹查询点赞 Top10 用户
zrevrange按分数从高到低取出数据,获取赞数最多的前 10 名:
zrevrange user:ranking:2022-03-15 0 9
🔹查询用户详情、分数与排名
用户基础信息存放在 Hash 结构中;通过zscore查看点赞分数,zrank查看当前排名:
hgetall user:info:tom
zscore user:ranking:2022-03-15 mike
zrank user:ranking:2022-03-15 mike
六、总结
String 是基础,缓存、计数、共享 Session 都能用;Hash 适合存对象;List 有序,做简易消息队列、时间线;Set 自动去重,适合标签;Zset 带分值排序,主打排行榜。Redis 适合高速读写,复杂关联查询交给 MySQL 就行。
另外,本文只列举了各个数据类型的常用命令,其实redis中的命令很多,如果需要用到更多命令,建议去redis官网搜索学习。