Redis数据结构以及使用场景

在没有redis之前,客户端访问后端应用时,当并发大的时候,存储层如mysql是支撑不了的,可能会将存储层mysql压死,存储层一旦宕机,整个应用就完了。为了很高效的加速应用的读写速度,同时也可以降低后端负载在一定程度上可以通过AOF和RDB机制保证在一定的情况下(如缓存层宕机)快速恢复数据为应用提供服务。缓存层可以通过主从复制+哨兵或集群实现高可用。Redis(REmote DIctionary Server)是一个开源的使用ANSI C语言编写的基于内存亦可持久化的日志型,key-value数据库,并提供了多种语言。它通常被称为数据结构服务器,因为值可以是string,hash,list,set,zset. Redis采用了单线程架构和I/O多路复用模型来实现高性能的内存数据库服务,Redis执行命令的速度非常快,官方给出的数字是读写性能可以达到10万/秒,在单线程的架构下为什么能如此快呢?主要有四点原因:①redis的所有数据是存放在内存中的②redis是C语言实现的,C语言实现的程序距离操作系统更近③Redis使用了单线程架构,预防了多线程可能产生的竞争问题,例如,CPU频繁切换,资源竞争等问题。④Redis源代码非常少,可以说是精打细磨的。

基本数据结构


1.字符串(String): 

可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB 
命令
添加元素
//ex:秒级过期时间,nx:键不存在时才能设置成功,xx键存在时才能设置成功
set key value [ex seconds] [px milliseconds] [nx|xx]

获取值

get key


批量设置值

mset key value [key value ...]
mset a 1 b 2 c 3 d 4

批量获取值

mget key [key ...]
mget a b c d

计数

incr key

追加值

append key value

字符串长度

strlen key

设置并返回原值

getset key value

设置指定位置的字符

setrange key offeset value
//例子
 set redis pest
 setrange redis 0 b
 get redis
 结果:"best"

获取部分字符串

getrange key start end

内部编码
i. int 长整型
ii. embstr 短字符(<=39字节)
iii. raw 长字符(>39)
使用场景
i. 缓存

//  定义键
userRedisKey = "user:info:" + id;
//  从 Redis 获取值
value = redis.get(userRedisKey);
if (value != null) {
//  将值进行反序列化为 UserInfo 并返回结果
userInfo = deserialize(value);
return userInfo;
}

ii. 计数:点赞,视频播放量,每播放一次就+1

long incrVideoCounter(long id) {
key = "video:playCount:" + id;
return redis.incr(key);
}

iii. 接口防刷:验证码登录,公司一般的验证码等发短信功能都是调用的第三方接口,如果被有心之人利用了,会给公司造成一定的损失,所以简单的解决方法就是限流

phoneNum = "138xxxxxxxx";
key = "shortMsg:limit:" + phoneNum;
// SET key value EX 60 NX
isExists = redis.set(key,1,"EX 60","NX");
if(isExists != null || redis.incr(key) <=5){
//  通过
}else{
//  限速
}

**2.哈希(hash):**哈希类型是指键值本身又是一个键值对结构,value={{field1,value1},...{fieldN,valueN}} ![这里写图片描述](https://img-blog.csdn.net/20180607210008695?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poMTU3MzI2MjE2Nzk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
命令
//设置值
hset key field value

//获取值
hget key field

//删除field
hdel key field [field ...]

//计算field个数
hlen key

//批量设置或获取field-value
hmset key field value [field value ...]
hmget key field [field ...]

//判断field是否存在
hexists key field

//获取所有field
hkeys key

//获取所有value
hvals key

//获取所有的field-value
hgetall key

//以某种形式自增
hincrby key field
hincrbyfloat key field

//计算value的字符串长度
hstrlen key field

内部编码
i. ziplist:当field个数不超过hash-max-ziplist-entries(默认为512个)时,并且没有大value(64个字节以上算大)
ii. hashtable:ziplist的两个条件只要有一个不符合就会转换为hashtable
使用场景
i. 存储用户信息
§ 原生string:直观,占用键多
§ 序列化字符串:序列化后好操作,每次都需要反序列化和序列化所有字段
§ 哈希类型:简单直观,减少内存空间的使用

**3.列表(list)?*用来存储多个有序的字符串,列表中的每个字符串称为元素(element),一个列表最多可以存储2^32 -1个元素

命令
//从右边插入元素
rpush key value [value ...]

//从左边插入元素
lpush key value [value ...]

//向某个元素前或者后插入元素
linsert key before|after pivot value

//获取指定范围内的元素列表
lrange key start end

//获取列表指定索引下标的元素
lindex key index

//获取列表长度
llen key

//从列表左侧弹出元素
lpop key

//从列表右侧弹出
rpop key

//删除指定元素,count>0从左到右,count<0从右到左,count=0,删除所有
lrem key count value

//按照索引范围修剪列表
ltrim key start end

//修改指定索引下标的元素:
lset key index newValue

//阻塞式弹出
//列表为空,则按照设置的timeout值进行阻塞
//列表不为空,则会立即返回
blpop key [key ...] timeout
brpop key [key ...] timeout

内部编码
i. ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置
(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时
(默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使
用。
ii. linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用
linkedlist作为列表的内部实现。
使用场景
i. 消息队列:Redis的lpush+brpop命令组合即可实现阻塞队列

**4.集合(set)?*用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。

命令
添加元素
sadd key element ...
sadd myset a b c

删除元素

srem key element [element ...]
//返回成功删除元素个数
srem myset a b

计算元素个数

scard key

判断元素是否在集合中

sismember key element

随机从集合返回指定个数元素

srandmember key [count]

从集合随机弹出元素

spop key

获取所有元素

smembers key

多个集合求交集

sinter key [key ...]

多个集合求并集

suinon key [key ...]

求多个集合的差集

sdiff key [key ...]

将交集、并集、差集的结果保存

sinterstore destination key [key ...]
suionstore destination key [key ...]
sdiffstore destination key [key ...]

内部编码
i. intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-
intset-entries配置(默认512个)时,Redis会选用intset来作为集合的内部实
现,从而减少内存的使用。
ii. hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使
用hashtable作为集合的内部实现。
使用场景
i. 给用户添加标签
sadd user:1:tags tag1 tag2 tag5

ii.抽奖
生成随机数,集合不能存放相同的元素,因此随机pop出一个元素,可以作为中奖的号码
iii. 社交需求:可以给用户推荐有相同兴趣的人,用于交友。

**5.有序集合(zset)?*不能有重复的元素,而且还可以排序,它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据


命令
添加成员
zadd key score member
zadd user:ranking 251 tom

计算成员个数

zcard key
zcard user:ranking

计算某个成员的分数,如果成员不存在,则返回nil

zscore key member
zscore user:ranking tom

计算成员的排名,zrank是分数从低到高,zrevrank从高到低

zrank key member
zrevrank key member

删除成员

zrem key member
//将成员mike从有序集合user:ranking中删除
zrem user:ranking mike

增加成员的分数

zincrby key increment member
//给tom增加了9分,分数变为了260分
zincrby user:ranking 9 tom

返回指定排名范围的成员

//从低到高返回
zrange key start end [withscores]
//从高到低返回
zrevrange key start end [withscores]
//返回排名最低的是三个成员
zrange user:ranking 0 2 withscores
1) "kris"
2) "1"
3) "frank"
4) "200"
5) "tim"
6) "220"

返回指定分数范围的成员

zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withscores] [limit offset count]

返回指定分数范围成员个数

zcount key min max

删除指定排名内的升序元素

zremrangebyrank key start end

删除指定分数范围的成员

zremrangebyscore key min max

求两个集合交集

zinterstore destination numkeys key [key ...] [weights weight [weight ...]]
[aggregate sum|min|max]
//求交集,结果保存到user:ranking:1_inter_2
zinterstore user:ranking:1_inter_2 2 user:ranking:1
user:ranking:2

求并集

zunionstore destination numkeys key [key ...] [weights weight [weight ...]]
[aggregate sum|min|max]
//求并集
zunionstore user:ranking:1_union_2 2 user:ranking:1
user:ranking:2

内部编码
i. ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplist-entries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使用。
ii. skiplist(跳跃表):当ziplist条件不满足时,有序集合会使用skiplist作为内部实现,因为此时ziplist的读写效率会下降。
使用场景
i. 用户点赞数排行
ii. 排行榜
iii. 展示用户信息及用户分数(例如学校中按学生分数排序)
 

一、字符串
字符串类型是redis最基础的数据结构,首先键是字符串类型,而且其他几种结构都是在字符串类型基础上构建的,
所以字符串类型能为其他四种数据结构的学习尊定基础。
字符串类型实际上可以是字符串
(简单的字符串、复杂的字符串(xml、json)、数字(整数、浮点数)、二进制(图片、音频、视频)),
但最大不能超过512M。

使用场景:
缓存功能:字符串最经典的使用场景,redis最为缓存层,Mysql作为储存层,绝大部分请求数据都是
         redis中获取,由于redis具有支撑高并发特性,所以缓存通常能起到加速读写和降低 后端压力的作用。
        (redis为何具备支撑高并发的特性,下次文章讲解)。
计数器:许多运用都会使用redis作为计数的基础工具,他可以实现快速计数、查询缓存的功能,
        同时数据可以一步落地到其他的数据源。
        如:视频播放数系统就是使用redis作为视频播放数计数的基础组件。
共享session:出于负载均衡的考虑,分布式服务会将用户信息的访问均衡到不同服务器上,
        用户刷新一次访问可能会需要重新登录,为避免这个问题可以用redis将用户session集中管理,
        在这种模式下只要保证redis的高可用和扩展性的,每次获取用户更新或查询登录信息
        都直接从redis中集中获取。
限速:处于安全考虑,每次进行登录时让用户输入手机验证码,为了短信接口不被频繁访问,
        会限制用户每分钟获取验证码的频率。


二、哈希
在redis中哈希类型是指键本身又是一种键值对结构,如 value={{field1,value1},......{fieldN,valueN}}  

使用场景:
      哈希结构相对于字符串序列化缓存信息更加直观,并且在更新操作上更加便捷。
      所以常常用于**用户信息**等管理,但是哈希类型和关系型数据库有所不同,哈希类型是稀疏的,
      而关系型数据库是完全结构化的,关系型数据库可以做复杂的关系查询,而redis去模拟关系型复杂查询
      开发困难,维护成本高。

三、列表
列表类型是用来储存多个有序的字符串,列表中的每个字符串成为元素(element),一个列表最多可以储存
2的32次方-1个元素,在redis中,可以队列表两端插入(pubsh)和弹出(pop),还可以获取指定范围的元素
列表、获取指定索引下表的元素等,列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,
在实际开发中有很多应用场景。
优点:
    1.列表的元素是有序的,这就意味着可以通过索引下标获取某个或某个范围内的元素列表。
    2.列表内的元素是可以重复的。

使用场景:
消息队列: redis的lpush+brpop命令组合即可实现阻塞队列,生产者客户端是用lupsh从列表左侧插入元素,
         多个消费者客户端使用brpop命令阻塞时的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡
         和高可用性

 
消息队列模型↑

文章列表:每个用户都有属于自己的文章列表,现在需要分页展示文章列表,此时可以考虑使用列表,列表不但有序
         同时支持按照索引范围获取元素。

使用列表技巧: 
lpush+lpop=Stack(栈) 
lpush+rpop=Queue(队列) 
lpush+ltrim=Capped Collection(有限集合) 
lpush+brpop=Message Queue(消息队列)

四、集合
集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中不允许有重复的元素,并且集合中的元素是
无序的,不能通过索引下标获取元素,redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、
差集,并合理的使用好集合类型,能在实际开发中解决很多实际问题。

使用场景:
 标签(tag):集合类型比较典型的使用场景,如一个用户对娱乐、体育比较感兴趣,另一个可能对新闻感兴
             趣,这些兴趣就是标签,有了这些数据就可以得到同一标签的人,以及用户的共同爱好的标签,
             这些数据对于用户体验以及曾强用户粘度比较重要。
             (用户和标签的关系维护应该放在一个事物内执行,防止部分命令失败造成数据不一致)

sadd=tagging(标签)
spop/srandmember=random item(生成随机数,比如抽奖)
sadd+sinter=social Graph(社交需求)

五、有序集合
有序集合和集合有着必然的联系,他保留了集合不能有重复成员的特性,但不同得是,有序集合中的元素是可以
排序的,但是它和列表的使用索引下标作为排序依据不同的是,它给每个元素设置一个分数,作为排序的依据。
(有序集合中的元素不可以重复,但是csore可以重复,就和一个班里的同学学号不能重复,但考试成绩可以相
同)。

列表、集合、有序集合三者的异同点

使用场景:
    排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:
           按照时间、按照播放量、按照获得的赞数等。

原文:

https://blog.csdn.net/zh15732621679/article/details/80614091

https://blog.csdn.net/qq_35568881/article/details/78779679

发布了737 篇原创文章 · 获赞 65 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_41723615/article/details/104205649
今日推荐