【总结】Redis五大数据类型应用场景及基本命令

目录

[一、String 字符串类](#一、String 字符串类)

1.常用基础命令

2.经典应用场景

✅缓存(cache)

✅计数功能(counter)

✅共享会话(session)

[二、Hash 哈希类](#二、Hash 哈希类)

1.常用基础命令

2.经典应用场景

✅表示关系型数据表

[三、List 列表类](#三、List 列表类)

1.常用基础命令

2.经典应用场景

✅消息队列

[✅Redis 分频道阻塞消息队列模型](#✅Redis 分频道阻塞消息队列模型)

[四、Set 集合类](#四、Set 集合类)

1.常用基础命令

2.经典应用场景

✅标签

[五、Zset 有序集合类](#五、Zset 有序集合类)

1.常用基础命令

2.经典应用场景

✅排行榜

六、总结


如前文所说,redis的五大数据类型是指的键(key)所对应的值(value)值的数据类型,分别是String、Hash、List、Set、Zset这五类,本文将介绍如何创建及使用这五种数据类型,并简单介绍其应用场景。

一、String 字符串类

字符串数据类型是redis中最基础的数据类型,需要注意的是:

  1. redis中所有的key都是字符串类型,并且其他4中数据类型也是在字符串的基础上构建的;(例如列表和集合的元素类型就是字符串。所以字符串类型也为我们学习其他4类数据类型提供支撑。)
  2. 字符串的一般表现形式有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 中获取。

  1. 用户通过向业务层发送get请求,等待响应。
  2. 业务层收到请求后先在查看缓存层(redis)中有没有该数据
  3. 若有,则直接返回给业务层,业务层再将拿到的数据响应给用户。
  4. 若没有,则到存储层(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官网搜索学习。

相关推荐
我不会起名字3221 小时前
Redis pipeline 用了反而更慢:3 类误用与正确的批量姿势
redis·pipeline·lua·rtt·批量
Gauss松鼠会1 小时前
GaussDB 整体架构解析
运维·服务器·网络·数据库·sql·架构·gaussdb
后台模板学习1 小时前
Go Web开发中任务调度框架的源码解析与高可用实现方案
开发语言·后端·golang
系统集成-鹰翔1 小时前
ERP对接升级提示数据库迁移失败:SQLite逐版本事务到底回滚到哪一步
javascript·数据库·经验分享·后端
西瓜拿铁好喝1 小时前
开源和闭源 AI 办公工具,分别适合谁
java·前端·数据库
IT_陈寒1 小时前
深入理解SpringBoot:核心原理与最佳实践
前端·人工智能·后端
学长毕设1 小时前
基于SpringBoot的校园礼服租赁系统的设计与实现
java·spring boot·后端
代码派1 小时前
业务不停机迁移 MySQL 到 TiDB,NineData 数据增量同步实操演示
数据库·mysql·tidb·ninedata·无停机迁移·增量迁移·全量迁移
Gauss松鼠会1 小时前
GaussDB 中 gsql 与 DBeaver(JDBC)客户端工具查询时间不一致:原因、排查与处理
运维·服务器·数据库·sql·gaussdb·经验总结