共计 7607 个字符,预计需要花费 20 分钟才能阅读完成。
这篇文章主要介绍“Redis 常用数据结构有哪些及怎么实现”,在日常操作中,相信很多人在 Redis 常用数据结构有哪些及怎么实现问题上存在疑惑,丸趣 TV 小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Redis 常用数据结构有哪些及怎么实现”的疑惑有所帮助!接下来,请跟着丸趣 TV 小编一起来学习吧!
Redis 常用数据结构
Redis 提供了一些数据结构供我们往 Redis 中存取数据,最常用的的有 5 种,字符串(String)、哈希(Hash)、列表(list)、集合(set)、有序集合(ZSET)。
字符串(String)
字符串类型是 Redis 最基础的数据结构。首先键都是字符串类型,而且其他几种数据结构都是在字符串类型基础上构建的,所以字符串类型能为其他四种数据结构的学习奠定基础。字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如 JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过 512MB。
(虽然 Redis 是 C 写的,C 里面有字符串 本质使用 char 数组来实现,但是处于种种考虑,Redis 还是自己实现了字符串类型)
操作命令 set 设置值
set key value
set 命令有几个选项:
ex seconds: 为键设置秒级过期时间。
px milliseconds: 为键设置毫秒级过期时间。
nx: 键必须不存在, 才可以设置成功,用于添加(分布式锁常用)。
xx: 与 nx 相反, 键必须存在,才可以设置成功, 用于更新。
从执行效果上看,ex 参数和 expire 命令基本一样。还有一个需要特别注意的地方是如果一个字符串已经设置了过期时间,然后你调用了 set 方法修改了它,它的过期时间会消失。
而 nx 和 xx 执行效果如下
除了 set 选项,Redis 还提供了 setex 和 setnx 两个命令:
setex key
seconds value
setnx key value
setex 和 setnx 的作用和 ex 和 nx 选项是一样的。也就是,setex 为键设置秒级过期时间,setnx 设置时键必须不存在, 才可以设置成功。
setex 示例:
setnx 示例:
因为键 foo-ex 已存在, 所以 setnx 失败, 返回结果为 0,键 foo-ex2 不存在,所以 setnx 成功, 返回结果为 1。
有什么应用场景吗? 以 setnx 命令为例子,由于 Redis 的单线程命令处理机制,如果有多个客户端同时执行 setnx key value,根据 setnx 的特性只有一个客户端能设置成功,setnx 可以作为分布式锁的一种实现方案。当然分布式锁没有不是只有一个命令就 OK 了,其中还有很多的东西要注意,我们后面会用单独的章节来讲述基于 Redis 的分布式锁。
get 获取值
如果要获取的键不存在, 则返回 nil(空):
mset 批量设置值
通过 mset 命令一次性设置 4 个键值对
mget 批量获取值
批量获取了键 a、b、c、d 的值:
如果有些键不存在, 那么它的值为 nil(空),结果是按照传入键的顺序返回。
批量操作命令可以有效提高效率,假如没有 mget 这样的命令,要执行 n 次 get 命令具体耗时如下:
n 次 get 时间 = n 次网络时间 + n 次命令时间
使用 mget 命令后,要执行 n 次 get 命令操作具体耗时如下:
n 次 get 时间 = 1 次网络时间 + n 次命令时间
Redis 可以支撑每秒数万的读写操作,但是这指的是 Redis 服务端的处理能力,对于客户端来说,一次命令除了命令时间还是有网络时间,假设网络时间为 1 毫秒,命令时间为 0.1 毫秒(按照每秒处理 1 万条命令算),那么执行 1000 次 get 命令需要 1.1 秒(10001+10000.1=1100ms),1 次 mget 命令的需要 0.101 秒(11+10000.1=101ms)。
Incr 数字运算
incr 命令用于对值做自增操作, 返回结果分为三种情况:
值不是整数, 返回错误。
值是整数,返回自增后的结果。
键不存在,按照值为 0 自增, 返回结果为 1。
除了 incr 命令,Redis 提供了 decr(自减)、incrby(自增指定数字)、decrby(自减指定数字)、incrbyfloat(自增浮点数),具体效果请同学们自行尝试。
append 追加指令
append 可以向字符串尾部追加值
strlen 字符串长度
返回字符串长度
注意:每个中文占 3 个字节
getset 设置并返回原值
getset 和 set 一样会设置值, 但是不同的是,它同时会返回键原来的值
setrange 设置指定位置的字符
下标从 0 开始计算。
getrange 截取字符串
getrange 截取字符串中的一部分,形成一个子串,需要指明开始和结束的偏移量,截取的范围是个闭区间。
命令的时间复杂度
字符串这些命令中,除了 del、mset、mget 支持多个键的批量操作,时间复杂度和键的个数相关,为 O(n),getrange 和字符串长度相关,也是 O(n),其余的命令基本上都是 O(1)的时间复杂度,在速度上还是非常快的。
使用场景
字符串类型的使用场景很广泛:
缓存功能
Redis 作为缓存层,MySQL 作为存储层,绝大部分请求的数据都是从 Redis 中获取。由于 Redis 具有支撑高并发的特性, 所以缓存通常能起到加速读写和降低后端压力的作用。
计数
使用 Redis 作为计数的基础工具,它可以实现快速计数、查询缓存的功能, 同时数据可以异步落地到其他数据源。
共享 Session
一个分布式 Web 服务将用户的 Session 信息(例如用户登录信息)保存在各自服务器中,这样会造成一个问题,出于负载均衡的考虑,分布式服务会将用户的访问均衡到不同服务器上,用户刷新一次访问可能会发现需要重新登录,这个问题是用户无法容忍的。
为了解决这个问题, 可以使用 Redis 将用户的 Session 进行集中管理,,在这种模式下只要保证 Redis 是高可用和扩展性的, 每次用户更新或者查询登录信息都直接从 Redis 中集中获取。
限速
比如,很多应用出于安全的考虑, 会在每次进行登录时, 让用户输入手机验证码, 从而确定是否是用户本人。但是为了短信接口不被频繁访问, 会限制用户每分钟获取验证码的频率,例如一分钟不能超过 5 次。一些网站限制一个 IP 地址不能在一秒钟之内方问超过 n 次也可以采用类似的思路。
哈希(Hash)
Java 里提供了 HashMap,Redis 中也有类似的数据结构,就是哈希类型。但是要注意,哈希类型中的映射关系叫作 field-value,注意这里的 value 是指 field 对应的值,不是键对应的值。
操作命令
基本上,哈希的操作命令和字符串的操作命令很类似,很多命令在字符串类型的命令前面加上了 h 字母,代表是操作哈希类型,同时还要指明要操作的 field 的值。
hset 设值
hset user:1 name lijin
如果设置成功会返回 1,反之会返回 0。此外 Redis 提供了 hsetnx 命令,它们的关系就像 set 和 setnx 命令一样, 只不过作用域由键变为 field。
hget 取值
hget user:1 name
如果键或 field 不存在,会返回 nil。
hdel 删除 field
hdel 会删除一个或多个 field,返回结果为成功删除 field 的个数。
hlen 计算 field 个数
hmset 批量设值
hmget 批量取值
hexists 判断 field 是否存在
若存在返回 1,不存在返回 0
hkeys 获取所有 field
它返回指定哈希键所有的 field
hvals 获取所有 value
hgetall 获取所有 field 与 value
在使用 hgetall 时,如果哈希元素个数比较多,会存在阻塞 Redis 的可能。如果只需要获取部分 field,可以使用 hmget,如果一定要获取全部 field-value,可以使用 hscan 命令,该命令会渐进式遍历哈希类型,hscan 将在后面的章节介绍。
hincrby 增加
hincrby 和 hincrbyfloat,就像 incrby 和 incrbyfloat 命令一样,但是它们的作用域是 filed。
hstrlen 计算 value 的字符串长度
命令的时间复杂度
哈希类型的操作命令中,hdel,hmget,hmset 的时间复杂度和命令所带的 field 的个数相关 O(k),hkeys,hgetall,hvals 和存储的 field 的总数相关,O(N)。其余的命令时间复杂度都是 O(1)。
使用场景
从前面的操作可以看出,String 和 Hash 的操作非常类似,那为什么要弄一个 hash 出来存储。
哈希类型比较适宜存放对象类型的数据,我们可以比较下,如果数据库中表记录 user 为:
idnameage1lijin182msb20
1、使用 String 类型
需要一条条去插入获取。
set user:1:name lijin;
set user:1:age 18;
set user:2:name msb;
set user:2:age 20;
优点:简单直观,每个键对应一个值
缺点:键数过多,占用内存多,用户信息过于分散,不用于生产环境
2、将对象序列化存入 redis
set user:1 serialize(userInfo);
优点:编程简单,若使用序列化合理内存使用率高
缺点:序列化与反序列化有一定开销,更新属性时需要把 userInfo 全取出来进行反序列化,更新后再序列化到 redis
3、使用 hash 类型
hmset user:1 name lijin age 18
hmset user:2 name msb age 20
优点:简单直观,使用合理可减少内存空间消耗
缺点:要控制内部编码格式,不恰当的格式会消耗更多内存
列表(list)
列表 (list) 类型是用来存储多个有序的字符串,a、b、c、c、b 四个元素从左到右组成了一个有序的列表, 列表中的每个字符串称为元素 (element),一个列表最多可以存储(2^32-1) 个元素(4294967295)。
在 Redis 中,可以对列表两端插入 (push) 和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。
列表类型有两个特点:
第一、列表中的元素是有序的,这就意味着可以通过索引下标获取某个元素或者某个范围内的元素列表。
第二、列表中的元素可以是重复的。
操作命令 lrange 获取指定范围内的元素列表(不会删除元素)
key start end
索引下标特点:从左到右为 0 到 N -1
lrange 0 - 1 命令可以从左到右获取列表的所有元素
rpush 向右插入
lpush 向左插入
linsert 在某个元素前或后插入新元素
这三个返回结果为命令完成后当前列表的长度,也就是列表中包含的元素个数,同时 rpush 和 lpush 都支持同时插入多个元素。
lpop 从列表左侧弹出(会删除元素)
r
请注意,弹出来元素就没了。
rpop 从列表右侧弹出
rpop 将会把列表最右侧的元素 d 弹出。
lrem 对指定元素进行删除
lrem 命令会从列表中找到等于 value 的元素进行删除,根据 count 的不同分为三种情况:
count 0,从左到右, 删除最多 count 个元素。
count 0,从右到左, 删除最多 count 绝对值个元素。
count=0,删除所有。
返回值是实际删除元素的个数。
ltirm 按照索引范围修剪列表
例如想保留列表中第 0 个到第 1 个元素
ls
lset 修改指定索引下标的元素
lindex 获取列表指定索引下标的元素
l
llen 获取列表长度
blpop 和 brpop 阻塞式弹出元素
blpop 和 brpop 是 lpop 和 rpop 的阻塞版本,除此之外还支持多个列表类型,也支持设定阻塞时间,单位秒,如果阻塞时间为 0,表示一直阻塞下去。我们以 brpop 为例说明。
A 客户端阻塞了(因为没有元素就会阻塞)
A 客户端一直处于阻塞状态。此时我们从另一个客户端 B 执行
A 客户端则输出
注意:brpop 后面如果是多个键,那么 brpop 会从左至右遍历键,一旦有一个键能弹出元素,客户端立即返回。
使用场景
列表类型可以用于比如:
消息队列,Redis 的 lpush+brpop 命令组合即可实现阻塞队列,生产者客户端使用 lrpush 从列表左侧插入元素,多个消费者客户端使用 brpop 命令阻塞式的“抢”列表尾部的元素, 多个客户端保证了消费的负载均衡和高可用性。
文章列表
每个用户有属于自己的文章列表,现需要分页展示文章列表。此时可以考虑使用列表, 因为列表不但是有序的, 同时支持按照索引范围获取元素。
实现其他数据结构
lpush+lpop =Stack(栈)
lpush +rpop =Queue(队列)
lpsh+ ltrim =Capped Collection(有限集合)
lpush+brpop=Message Queue(消息队列)
集合(set)
集合(set)类型也是用来保存多个的字符串元素, 但和列表类型不一样的是,集合中不允许有重复元素, 并且集合中的元素是无序的, 不能通过索引下标获取元素。
一个集合最多可以存储 2 的 32 次方 - 1 个元素。Redis 除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集,合理地使用好集合类型, 能在实际开发中解决很多实际问题。
集合内操作命令 sadd 添加元素
允许添加多个,返回结果为添加成功的元素个数
srem 删除元素
允许删除多个,返回结果为成功删除元素个数
scard 计算元素个数
sismember 判断元素是否在集合中
如果给定元素 element 在集合内返回 1,反之返回 0
srandmember 随机从集合返回指定个数元素
指定个数如果不写默认为 1
spop 从集合随机弹出元素
同样可以指定个数,如果不写默认为 1,注意,既然是弹出,spop 命令执行后, 元素会从集合中删除, 而 srandmember 不会。
smembers 获取所有元素(不会弹出元素)
返回结果是无序的
集合间操作命令
现在有两个集合, 它们分别是 set1 和 set2
sinter 求多个集合的交集
suinon 求多个集合的并集
sdiff 求多个集合的差集
将交集、并集、差集的结果保存
sinterstore destination key [key ...]
suionstore destination key [key ...]
sdiffstore destination key [key ...]复制代码
集合间的运算在元素较多的情况下会比较耗时,所以 Redis 提供了上面三个命令 (原命令 +store) 将集合间交集、并集、差集的结果保存在 destination key 中,例如:
使用场景
集合类型比较典型的使用场景是标签(tag)。例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验以及增强用户黏度比较重要。
例如一个电子商务的网站会对不同标签的用户做不同类型的推荐,比如对数码产品比较感兴趣的人,在各个页面或者通过邮件的形式给他们推荐最新的数码产品,通常会为网站带来更多的利益。
除此之外,集合还可以通过生成随机数进行比如抽奖活动,以及社交图谱等等。
有序集合(ZSET)
有序集合相对于哈希、列表、集合来说会有一点点陌生, 但既然叫有序集合, 那么它和集合必然有着联系, 它保留了集合不能有重复成员的特性, 但不同的是, 有序集合中的元素可以排序。但是它和列表使用索引下标作为排序依据不同的是, 它给每个元素设置一个分数 (score) 作为排序的依据。
有序集合中的元素不能重复,但是 score 可以重复,就和一个班里的同学学号不能重复, 但是考试成绩可以相同。
有序集合提供了获取指定分数和元素范围查询、计算成员排名等功能,合理的利用有序集合,能帮助我们在实际开发中解决很多问题。
集合内操作命令 zadd 添加成员
返回结果代表成功添加成员的个数
要注意:
zadd 命令还有四个选项 nx、xx、ch、incr 四个选项
nx: member 必须不存在,才可以设置成功,用于添加。
xx: member 必须存在,才可以设置成功, 用于更新。
ch: 返回此次操作后, 有序集合元素和分数发生变化的个数
incr: 对 score 做增加,相当于后面介绍的 zincrby
zcard 计算成员个数
zscore 计算某个成员的分数
如果成员不存在则返回 nil
zrank 计算成员的排名
zrank 是从分数从低到高返回排名
zrevrank 反之
很明显,排名从 0 开始计算。
zrem 删除成员
允许一次删除多个成员。
返回结果为成功删除的个数。
zincrby 增加成员的分数
zrange 和 zrevrange 返回指定排名范围的成员
有序集合是按照分值排名的,zrange 是从低到高返回,zrevrange 反之。如果加上withscores 选项,同时会返回成员的分数
zrangebyscore 返回指定分数范围的成员
zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withscores][limit offset count]复制代码
其中 zrangebyscore 按照分数从低到高返回,zrevrangebyscore 反之。例如下面操作从低到高返回 200 到 221 分的成员,withscores 选项会同时返回每个成员的分数。
同时 min 和 max 还支持开区间(小括号)和闭区间(中括号),-inf 和 +inf 分别代表无限小和无限大:
zcount 返回指定分数范围成员个数
zcount key min max
zremrangebyrank 按升序删除指定排名内的元素
zremrangebyrank key start end
zremrangebyscore 删除指定分数范围的成员
zremrangebyscore key min max
集合间操作命令 zinterstore 交集
zinterstore
这个命令参数较多,下面分别进行说明
destination: 交集计算结果保存到这个键。
numkeys: 需要做交集计算键的个数。
key [key …]: 需要做交集计算的键。
weights weight[weight …]: 每个键的权重,在做交集计算时,每个键中的每个 member 会将自己分数乘以这个权重, 每个键的权重默认是 1。
aggregate sum/
min |max: 计算成员交集后,分值可以按照 sum(和)、min(最小值)、max(最大值)做汇总, 默认值是 sum。
不太好理解,我们用一个例子来说明。(算平均分)
zunionstore 并集
该命令的所有参数和 zinterstore 是一致的,只不过是做并集计算,大家可以自行实验。
到此,关于“Redis 常用数据结构有哪些及怎么实现”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注丸趣 TV 网站,丸趣 TV 小编会继续努力为大家带来更多实用的文章!