基础-redis五大数据类型及应用场景

文章目录

后续增加数据类型:bitmap(位图)、hyperloglog(统计)、geo(地理)

String(字符串)【单值单value】

string是redis最基本的类型,一个key对应一个value。string类型是二进制安全的,意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。一个redis中字符串value最大可以是512M。

常用命令

java 复制代码
set k1 v1
get k1
mset k1 v1 k2 v2
mget k1 k2
incr k3
incrby k3 2
decr k3
decrby k3 2
strlen k3
getrange k1 0 3			如果是字符串,则进行截取、如果是数字,则输出
setrange k1 0 aaaa	 	从下标0开始赋值,会替代字符串后面的字符,而不是后移
getset k1 v11111		getset(先get再set),前提是该key已经存在
setnx k5 v5				不存在则赋值,存在不赋值
setex k6 5 v6			键秒值,k6的生命周期只有5s

应用场景

1、业务缓存:比如说双十一预期数据库的访问压力陡增,可以利用Redis,提前将要被多次访问的数据放入redis,做到"缓存预热"。让用户进入我们活动页面的时候,先去搜索缓存,不直接访问数据库,做到释放数据库的压力。常见业务伪代码:

java 复制代码
void cacheData(){
    //1.从数据库中获取热点数据
    //2.将此类数据序列化
    //3.将 (data-data的id) 作为key,序列化的data作为value,利用string的set方法,存入redis。将活动的持续时间作为TTL
}
void getData(id){
    //1.将data和id拼接作为key,根据key聪redis中根据string的get指令,进行查询。
    //2.1查询到,直接返回
    //2.2没有查询到,根据id去数据库中查找,如果有,就直接返回,并存入redis中。
}

分布式锁:比如多个服务去抢夺资源,有并发,线程安全问题。可以利用redis的setnx命令,实现分布式锁的效果,伪代码:

java 复制代码
void tryLock(id){
//1.不断抢锁直到抢到
while(true){
//1.1.把 (update+传入的id)作为key,当前线程的名称作为value,使用nx指令,设置5s的过期时间(避免因为系统原因未能释放,则导致锁无法释放,这是兜底措施)
//1.2.1.如果1.1步骤失败,说明抢锁失败,进入下一次循环,继续抢锁。
//1.2.2.如果成功,就break,结束循环    
}
//2.执行业务逻辑
//3.删除 (update+传入的id)的key的值,相当于释放锁。
}

接口限流: 抢购、秒杀等高并发场景下,流量峰值很高,但是后端业务的资源很有限。解决方案:假如后端资源只够1000qps,那么我们可能就得对高于这个值的qps做限流,高于它的部分可能就得做降级处理了,伪代码如下:

java 复制代码
void limit(){
//1.取到当前的时间戳
Long now = currentTimeMilles()
//2.将当前服务名+now做为key,每打过来一个请求,就把这个value利用INCR指令+1。
//3.如果当前value > 1000 qps,那么就直接返回"请求限流"
//4.如果 value < 1000 qps,那么执行请求。
}

Hash(哈希,类似java里的Map)

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。类似Java里面的Map<String,Object>

常用命令:

java 复制代码
hset user id 11
hget user id
hgetall user
hmset stu name zhupeng age 23 
hmget student age name 		批量设置属性/批量获取属性值
hdel user id				删除指定属性
hexists user id				判断key里面的某个值的key
hkeys user					查询所有key值
hvals user					查询所有value值
hlen user					查询key长度
hincrby user id 2 			value值加num
hincrbyfloat user score 0.5
hsetnx user age 27			字段不存在则加入

应用场景:

对象数据存储场景:用户信息、商品信息、文章信息、购物车信息,比如购物车:

新增商品:hset shopcar:uid1024 334488 1

新增商品:hset shopcar:uid1024 334477 1

增加商品数量:hincrby shopcar:uid1024 334477 1

商品总数:hlen shopcar:uid1024

全部选择:hgetall shopcar:uid1024

List(列表)【单key多value且value可重复】

与其说list是一个集合,不如说是一个双端队列(双向列表),可以从列表的头部(左边)或者尾部(右边)插入或读取元素,支持反向查找和遍历,不过也带来了额外的内存开销。

常用命令:

java 复制代码
lpush list01 1 2 3 4 5		 类似入栈
lpop list01					从栈上边、队列左边读 // 5
rpop list01					从栈下边、队列右边读 // 1
rpush list02 1 2 3 4 5		 类似入队列
lpop list02					从栈上边、队列左边读 // 1
rpop list02					从栈下边、队列右边读 // 5
lset list01 0 111			给指定下标赋值,也即是替换
linsert list01 before/after 1 java 	在制定元素22之前/之后插入元素java
lrange list01 0 -1				从栈上边、队列左边开始查询集合list01 // 1 2 3 4 5
lindex list01 1 				按照索引下标获得元素(从栈上边、队列左边读)
llen list01						list01长度
lrem list01 2 3 				删除list01的2个3
ltrim list01 0 3				截取指定范围的值后再赋值给key
rpoplpush list01 list02			rpoplpush 源列表 目的列表

应用场景

1、消息队列:可以用来做消息队列,只是功能过于简单且存在很多缺陷,不建议这样做。相对来说,Redis 5.0 新增加的一个数据结构 Stream 更适合做消息队列一些,只是功能依然非常简陋。和专业的消息队列相比,还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。

2、点赞提醒:场景复现:我是一个掘金作者,当有掘友给我点赞的时候,我需要接收到点赞信息,并且给予反馈。解决方案:后端在接受点赞消息后,将点赞人push到点赞List中,当我(掘金作者)登录网站的时候,前端给后端请求点赞的list。

java 复制代码
void likeList(){
String listName = "like-" + articleId
//开启一个线程,模拟用户端点赞,此时有10000人
for(int i=0;i<10000;i++){
//将listName作为list的key,用户id作为value,利用rpush指令,加入list
}   
//再开启一个线程,模拟掘金作者端
while(true){
//1.利用LRANGE指令,拉取前1000个人的点赞。
//2.读取,并且展示。
//3.展示完成后,利用LTRIM指令,把第一步返回的userId的size大小作为下标1,-1作为下标2,执行该指令。相当于把前1000个userIds截断掉
}
}

性能总结:

它是一个字符串链表,left、right都可以插入添加;

如果键不存在,创建新的链表;

如果键已存在,新增内容;

如果值全移除,对应的键也就消失了。

链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了。

Set(集合)【单key多value且value不可重复】

Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺序但都唯一,有点类似于 Java 中的 HashSet 。当你需要存储一个列表数据,又不希望出现重复数据时,Set 是一个很好的选择,并且 Set 提供了判断某个元素是否在一个 Set 集合内的重要接口,这个也是 List 所不能提供的。

常用命令

java 复制代码
sadd set01 1 1 2 3 4 4  		添加set01= 1 2 3
smembers set01					查看set所有元素【不能用get】
sismember set01 1      			查看1是否是set01内元素【0不是,1 是】
scard set01						获取集合里面的元素个数
srem set01 1					删除集合中元素
srandmember set01 3			 	从集合中随机显示3个整数
spop set01 						集合中元素随机出栈
smove set01 set02 3				作用是将key1里的某个值赋给key2
sdiff set01 set02				差集,属于set01,不属于set02
sinter set01 set02				交集
sunion set01 set02				并集

应用场景

需要存放的数据不能重复的场景:网站UV 统计(数据量巨大的场景还是 HyperLogLog更适合一些)、文章点赞、动态点赞等场景。

相关命令:SCARD(获取集合数量) 。

需要获取多个数据源交集、并集和差集的场景:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集) 等场景。

相关命令:SINTER(交集)、SINTERSTORE (交集)、SUNION (并集)、SUNIONSTORE(并集)、SDIFF(差集)、SDIFFSTORE (差集)。

需要随机获取数据源中的元素的场景:抽奖系统、随机点名等场景。

相关命令:SPOP(随机获取集合中的元素并移除,适合不允许重复中奖的场景)、SRANDMEMBER(随机获取集合中的元素,适合允许重复中奖的场景)。

Zset(有序集合)【单key多value且value不可重复,有排序】

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。

常用命令

java 复制代码
zadd zset01 60 v1 70 v2 80 v3		增加
zrange zset01 0 -1					查询,不包括分数score
zrange zset01 0 -1 withscores 		查询,包括分数score
zrangebyscore zset01 60 90 			查询[60, 90]的value
zrangebyscore zset01 60 (90 		查询[60, 90)的value
zrangebyscore zset01 (60 (90 		查询(60, 90)的value
zrangebyscore zset01 60 90 limit 2 2	limit 开始下标步 多少步[Limit 作用是返回限制]
zrem zset01 v3					 	删除某score下对应的value值
zcard zset01   						查询个数
zrank zset01 v1 					查询下标
zrevrank key 						values值,作用是逆序获得下标值
zrevrange
zrevrangebyscore key 				结束score 开始score

应用场景

需要随机获取数据源中的元素根据某个权重进行排序的场景:各种排行榜比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。

相关命令:ZRANGE (从小到大排序)、 ZREVRANGE (从大到小排序)、ZREVRANK (指定元素排名)。

需要存储的数据有优先级或者重要程度的场景:比如优先级任务队列。

相关命令:ZRANGE (从小到大排序)、 ZREVRANGE (从大到小排序)、ZREVRANK (指定元素排名)。