技术分享——梳理下Redis五种数据类型和使⽤场景
写在前⾯:2020年⾯试必备的Java后端进阶⾯试题总结了⼀份复习指南在Github上,内容详细,图⽂并茂,有需要学习的朋友可以Star⼀下!
GitHub地址:
redis 五种数据类型和使⽤场景梳理!
Redis在我们⽇常⼯作中使⽤的⾮常频繁,但是也有⼈只会使⽤string类型,那么今天笔者梳理下redis中常⽤的5种数据类型,分别适⽤于哪些业务场景和基本操作,让⼤家以后能够在合适的缓存场景使⽤合适的数据类型。
String字符串类型
Redis⽀持的字符串类型不是定长分配的字符串,是动态变长字符串,修改字符串在没有增加特别多内容的情况下不需要重新分配内存空间,内部结构实现上有点类似于java的ArrayList,采⽤预分配冗余空间的⽅式来减少内存的频繁分配。
常⽤使⽤场景
字符串类型常⽤的场景有以下这些:
(1)缓存结构体信息:
将结构体json序列化成字符串,然后将字符串保存在redis的value中,将结构体的业务唯⼀标⽰作为key;这种保存json的⽤法⽤的最多的场景就是缓存⽤户信息,将⽤户bean信息转成json再序列化为字符串作为value保存在redis中,将⽤户id作为key。从代码中获取⽤户缓存信息就是⼀个逆过程,根据userid作为key获取到结构体json,然后将json转成java bean。
基本操作:
127.0.0.1:6379> set user.10001 {“id”:”10001”,”name”:”monkey”}
(integer) 1
(2)计数功能:
我们都知道redis是单线程模式,并且redis将很多常⽤的事务操作进⾏了封装,这⾥我们最常⽤的就是数值⾃增或⾃减,redis的作者封装了incr可以进⾏⾃增,没调⽤⼀次⾃增1,因为redis是单线程运⾏,所以就算client是多线程调⽤那么也是正确⾃增,因为incr命令中将read和write做了事务封装。同样可
以设置incr的step,每次根据step进⾏⾃增,当然如果要达到⾃减的效果,那么只要将step设置为负数就可以了。
计数功能使⽤的场景很多,我们之前经常⽤在实时计数统计场景,也⽤在过库存场景、限流计数场景等等,⽽且redis的性能也是⾮常⾼的,对于⼀般的并发量没那么⾼的系统都是适⽤的。
基本操作:
127.0.0.1:6379> set num 1
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> incrby num 2
(integer) 4
List列表类型
redis的列表的数据结构和Java中的LinkedList⽐较类似,所以List类型的前后插⼊和删除速度是⾮常快
的,但是随机定位速度⾮常慢,时间复杂度是O(n)需要对列表进⾏遍历。
常⽤使⽤场景
(1)list列表结构常⽤来做异步队列使⽤
将需要延后处理的任务结构体序列化成字符串塞进 Redis 的列表,另⼀个线程从这个列表中轮询数据进⾏处理。
(2)list可⽤于秒杀抢购场景
在商品秒杀场景最怕的就是商品超卖,为了解决超卖问题,我们经常会将库存商品缓存到类似MQ的队列中,多线程的购买请求都是从队列中取,取完了就卖完了,但是⽤MQ处理的化有点重,这⾥就可以使⽤redis的list数据类型来实现,在秒杀前将本场秒杀的商品放到list中,因为list的pop操作是原⼦性的,所以即使有多个⽤户同时请求,也是依次pop,list空了pop抛出异常就代表商品卖完了。
基本操作:
//库存为3瓶可乐
> rpush goods:cola cola cola cola
(integer) 3
> lpop goods:cola
"cola"
> lpop goods:cola
"cola"
Hash数据类型
redis的hash相当于hashmap,内部实现上和hashmap⼀致,数组+链表的数据结构。
redis的hash数据类型只能是字符串。它们 rehash 的⽅式不⼀样,因为 Java 的 HashMap 在字典很⼤时,rehash 是个耗时的操作,需要⼀次性全部 rehash。Redis 为了⾼性能,不能堵塞服务,所以采⽤了渐进式 rehash 策略。渐进式 rehash 会在 rehash 的同时,保留新旧两个 hash 结构,查询时会同时查询两个 hash 结构,然后在后续的定时任务中以及 hash 操作指令中,循序渐进地将旧 hash 的内容⼀点点迁移到新的 hash 结构中。当搬迁完成了,就会使⽤新的hash结构取⽽代之。 当 hash 移除了最后⼀个元素之后,该数据结构⾃动被删除,内存被回收。
常⽤使⽤场景
(1)保存结构体信息
hash字典类型也是⽐较适合保存结构体信息的,不同于字符串⼀次序列化整个对象,hash可以对⽤户结构中的每个字段单独存储。这样当我们需要获取结构体信息时可以进⾏部分获取,⽽不⽤序列化所有字段,⽽将整个字符串保存的结构体信息只能⼀次性全部读取。
基本操作:
127.0.0.1:6379> hset user.10002 name monkey
(integer) 1
127.0.0.1:6379> hget user.10002 name
"monkey"
127.0.0.1:6379> hgetall user.10002
1) "id"
2) "10002"
3) "name"
4) "monkey"
Set集合类型
redis的set相当于java中的HashSet,内部的健值是⽆序唯⼀的,相当于⼀个hashmap,但是value都是null。set数据类型其实没什么好讲的,使⽤场景也是⽐较单⼀的,就是⽤在⼀些去重的场景⾥,例如每个⽤户只能参与⼀次活动、⼀个⽤户只能中奖⼀次等等去重场景。
基本操作
127.0.0.1:6379> sadd userset 10001
(integer) 1
127.0.0.1:6379> sadd userset 10002
(integer) 1
127.0.0.1:6379> sadd userset 10001redis支持的五种数据类型
(integer) 0
127.0.0.1:6379> sadd userset 10003 10004
(integer) 2
127.0.0.1:6379> smembers userset
1) "10001"
2) "10002"
3) "10003"
4) "10004"
Zset有序集合
它类似于 Java 的 SortedSet 和 HashMap 的结合体,⼀⽅⾯它是⼀个 set,保证了内部 value 的唯⼀性,另⼀⽅⾯它可以给每个 value 赋予⼀个 score,代表这个 value 的排序权重。zset内部是通过跳跃列表这种数据结构来实现的。因为zset要⽀持随机的插⼊和删除,所以不能使⽤数组结构,⽽需要改成普通链表数据结构。zset需要根据score进⾏排序,所以每次插⼊或者删除值都需要进⾏先在链表上查定位。
常⽤使⽤场景
(1)各类热门排序场景
例如热门歌曲榜单列表,value值是歌曲ID,score是播放次数,这样就可以对歌曲列表按播放次数进⾏排序。
当然还有类似微博粉丝列表、评论列表等等,可以将value定义为⽤户ID、评论ID,score定义为关注时间、评论点赞次数等等。
基本操作:
这⾥的例⼦就是对⽤户的评分进⾏排序。
127.0.0.1:6379> zadd userzset 100 10002
(integer) 1
127.0.0.1:6379> zadd userzset 98 10001
(integer) 1
127.0.0.1:6379> zrange userzset 0 100
1) "10001"
2) "10002"
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论