Redis核心数据结构实战
文章目录
- 概述
- 一、String运用场景
- 1.1、单值缓存
- 1.2、对象缓存
- 1.3、计数器
- 1.4、批量生成序号
- 二、Hash
- 2.1、对象缓存
- 2.2、临时购物车
- 三、List
- 3.1、实现栈、队列、阻塞队列
- 3.2、消息推送
- 四、Set
- 4.1、抽奖
- 4.2、点赞、收藏
- 4.3、集合操作
- 五、ZSet
概述
Redis有五种核心数据结构:
- String:标准的 key-value 结构:key -> value
- Hash:key -> field1:value1, field2:value2,类似双层 Map
- List:key -> 有序列表 [v1, v2, v3, …]
- Set:key -> 无序、不重复元素集合 {v1, v2, v3}
- Zset:也称为sorted_set,key -> (member, score) 的集合,自动按 score 排序
一、String运用场景
String通常是项目中运用最多的场景,通常可用于单值缓存,对象缓存,计数器,生成ID序列
等场景:
1.1、单值缓存
可运用于邮件验证码,短信验证码等临时信息的存储,这类数据的生命周期较短,没有必要存储在数据库中,在设置键时,通常是业务名称:用户手机号/邮箱
这样的格式。
phone:13211111111
email:13211111111@126.com
1.2、对象缓存
对象缓存和单值缓存的区别在于,对象缓存的value通常是转换为JSON格式的Java对象,在存储时进行序列化,查询时进行反序列化。
可用于缓存页面上列表的查询条件,键可以设置为业务名称:用户ID:页面上选择的查询条件
fundlist:1:查询条件1_查询条件2_查询条件3
假设我有如下的数据:
id | name | age |
---|---|---|
1 | zhagnsan | 23 |
2 | lisi | 20 |
3 | wanger | 15 |
可以有两种存储的方式,方式一:
- key:业务名称:用户ID
- value:该用户ID对应的name和age,在java中是转换为json字符串的。
127.0.0.1:6379> SET user:1 "name:zhangsan age:23"
OK
127.0.0.1:6379> get user:1
"name:zhangsan age:23"
方式二,使用mset
命令:
- key:业务名称:用户ID:某个属性名
- value:该用户ID对应的属性名的属性值
127.0.0.1:6379> mset user:2:name zhangsan user:2:age 23
OK
127.0.0.1:6379> mset user:2:name lisi user:2:age 20
OK
127.0.0.1:6379> mget user:2:name
1) "lisi"
第二种方式的粒度更小,比第一种方式更加便于修改。第一种方式,存储的对象是一个整体,如果需要修改,则经过反序列化->修改->重新序列化的过程
。而第二种方式细化到了对象的某个属性,针对于属性频繁修改的场景。
1.3、计数器
可以运用在文章阅读
的计数,或者自增序列
,该操作由Redis单线程保证原子性:
127.0.0.1:6379> INCR article:readcount:1
(integer) 1
127.0.0.1:6379> INCR article:readcount:1
(integer) 2
127.0.0.1:6379> INCR article:readcount:1
(integer) 3
127.0.0.1:6379> get article:readcount:1
"3"
1.4、批量生成序号
通过INCRBY
命令获取批量序号,存入内存中进行分配使用:
127.0.0.1:6379> INCRBY orderId 1000
(integer) 1000
127.0.0.1:6379> get orderId
"1000"
二、Hash
Hash在项目中的运用场景,可以是对象缓存,以及临时购物车功能。Hash结构不建议存储单个key的value过大的数据,否则在集群环境下会造成数据倾斜。
2.1、对象缓存
相比较于String,Hash适合结构扁平、字段不多、经常需要单独修改或读取某个字段的场景:
127.0.0.1:6379> hset user:3 name wanger age 18
(integer) 0
127.0.0.1:6379> hget user:3 name
"wanger"
2.2、临时购物车
电商网站,销售平台的临时购物车,也可以使用Redis的Hash结构进行存储:
- key:业务名称:用户ID
- field:商品ID
- value:商品数量
加入购物车:
127.0.0.1:6379> hset cart:1 1 1
(integer) 1
127.0.0.1:6379> hset cart:1 2 5
(integer) 1
从购物车获取元素:
127.0.0.1:6379> hget cart:1 2
"5"
增加购物车数量
127.0.0.1:6379> HINCRBY cart:1 1 1
(integer) 2
购物车中一共有多少种类的商品
127.0.0.1:6379> HLEN cart:1
(integer) 2
删除某一样商品
127.0.0.1:6379> hdel cart:1 1
(integer) 1
获取购物车的所有商品
127.0.0.1:6379> HGETALL cart:1
1) "2"
2) "5"
三、List
List结构常用于实现分布式场景下的栈,队列,阻塞队列
。在社交项目中,还可以实现消息的推送和接收
:
3.1、实现栈、队列、阻塞队列
实现先进后出
的栈:
127.0.0.1:6379> lpush stack a
(integer) 1
127.0.0.1:6379> lpush stack b
(integer) 2
127.0.0.1:6379> lpush stack c
(integer) 3
127.0.0.1:6379> lpop stack
"c"
实现先进先出
的队列:
127.0.0.1:6379> lpush queue 1
(integer) 1
127.0.0.1:6379> lpush queue 1
(integer) 2
127.0.0.1:6379> rpop queue
"1"
实现阻塞
队列,需要设置超时时间
127.0.0.1:6379> lpush blocking_queue "1"
(integer) 1
127.0.0.1:6379> lpush blocking_queue "2"
(integer) 2
127.0.0.1:6379> brpop blocking_queue 5
1) "blocking_queue"
2) "1"
127.0.0.1:6379> brpop blocking_queue 5
1) "blocking_queue"
2) "2"
127.0.0.1:6379> brpop blocking_queue 5
(nil)
(5.01s)
3.2、消息推送
当微信公众号发送新文章时,需要推送给每一个订阅的用户:
- key:业务名称:用户ID
- value:消息ID
127.0.0.1:6379> LPUSH msg:1 1926
(integer) 1
127.0.0.1:6379> LPUSH msg:1 1848
(integer) 2
127.0.0.1:6379> LRANGE msg:1 0 4
1) "1848"
2) "1926"
客户端拿到消息ID后进行处理
四、Set
Set在项目中可以用于抽奖、点赞,收藏,以及共同关注
的场景。
4.1、抽奖
用户在页面上点击一次抽奖,就向set集合中存放一个元素,利用set去重的特性,防止同一用户多次参与:
127.0.0.1:6379> sadd cj 1 2 3 4 5
(integer) 5
127.0.0.1:6379> sadd cj 1 2 3 4 5
(integer) 0
查看抽奖的用户
127.0.0.1:6379> SMEMBERS cj
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
进行抽奖(SRANDMEMBER不会删除set中的元素,而SPOP会)
127.0.0.1:6379> SRANDMEMBER cj 2
1) "4"
2) "5"
4.2、点赞、收藏
点赞,收藏同样利用的是sadd、smembers
等命令:
127.0.0.1:6379> sadd like:1848 1
(integer) 1
取消点赞:
127.0.0.1:6379> SREM like:1848 1
(integer) 1
查询用户是否点过赞
127.0.0.1:6379> sismember like:1848 1
(integer) 1
获取点赞的用户列表:
127.0.0.1:6379> SMEMBERS like:1848
1) "1"
获取点赞的用户数量:
127.0.0.1:6379> SCARD like:1848
(integer) 1
4.3、集合操作
集合操作有交集,并集,差集
,首先定义三个集合:
127.0.0.1:6379> sadd set1 a b c
(integer) 3
127.0.0.1:6379> sadd set2 b c
(integer) 2
127.0.0.1:6379> sadd set3 b c d e
(integer) 4127.0.0.1:6379> SMEMBERS set1
1) "b"
2) "c"
3) "a"
127.0.0.1:6379> SMEMBERS set2
1) "b"
2) "c"
127.0.0.1:6379> SMEMBERS set3
1) "e"
2) "b"
3) "c"
4) "d"
交集
是求集合1和集合2共有的部分:
127.0.0.1:6379> SINTER set1 set2
1) "b"
2) "c"
并集
是求集合1和集合2所有的部分:
127.0.0.1:6379> sunion set1 set2
1) "b"
2) "a"
3) "c"
差集
返回所有存在于第一个集合,但不存在于其他一个或多个集合中的成员:
127.0.0.1:6379> sdiff set1 set2 set3
1) "a"
五、ZSet
ZSet也称为sorted_set,继承了Set的功能之外,增加了排序的功能,可使用于项目中的排行榜功能。
点击新闻:
127.0.0.1:6379> ZINCRBY hotnews:20250622 1 hotnews1
"1"
127.0.0.1:6379> ZINCRBY hotnews:20250622 1 hotnews1
"2"
127.0.0.1:6379> ZINCRBY hotnews:20250622 1 hotnews1
"3"
127.0.0.1:6379> ZINCRBY hotnews:20250622 1 hotnews1
"4"
127.0.0.1:6379> ZINCRBY hotnews:20250622 1 hotnews2
"1"
127.0.0.1:6379> ZINCRBY hotnews:20250622 1 hotnews2
"2"
127.0.0.1:6379> ZINCRBY hotnews:20250622 1 hotnews3
"1"
展示当日排行
127.0.0.1:6379> ZREVRANGE hotnews:20250622 0 10 withscores
1) "hotnews1"
2) "4"
3) "hotnews2"
4) "2"
5) "hotnews3"
6) "1"
展示多日榜单
127.0.0.1:6379> ZINCRBY hotnews:20250621 1 hotnews3
"1"
127.0.0.1:6379> ZINCRBY hotnews:20250620 1 hotnews3
"1"
127.0.0.1:6379> ZREVRANGE hotnews:20250620-20250622 0 10 withscores
1) "hotnews1"
2) "4"
3) "hotnews3"
4) "3"
5) "hotnews2"
6) "2"