redis 5种数据结构和业务应用场景
wptr33 2024-12-31 15:02 45 浏览
Redis基础数据结构有哪些?
一、String(字符串)
在任何一种编程语言里,字符串String都是最基础的数据结构, 那你有想过Redis中存储一个字符串都进行了哪些操作嘛?
在Redis中String是可以修改的,称为动态字符串(Simple Dynamic String 简称 SDS)(快拿小本本记名词,要考的),说是字符串但它的内部结构更像是一个 ArrayList,内部维护着一个字节数组,并且在其内部预分配了一定的空间,以减少内存的频繁分配。
Redis的内存分配机制是这样:
- 当字符串的长度小于 1MB时,每次扩容都是加倍现有的空间。
- 如果字符串长度超过 1MB时,每次扩容时只会扩展 1MB 的空间。
这样既保证了内存空间够用,还不至于造成内存的浪费,字符串最大长度为 512MB.。
以上图片源自网络,如有侵权联系删除
上图就是字符串的基本结构,其中 content 里面保存的是字符串内容,0x\0作为结束字符不会被计算len中。
分析一下字符串的数据结构
struct SDS{
T capacity; //数组容量
T len; //实际长度
byte flages; //标志位,低三位表示类型
byte[] content; //数组内容
}
复制代码capacity 和 len两个属性都是泛型,为什么不直接用int类型?因为Redis内部有很多优化方案,为更合理的使用内存,不同长度的字符串采用不同的数据类型表示,且在创建字符串的时候 len 会和 capacity 一样大,不产生冗余的空间,所以String值可以是字符串、数字(整数、浮点数) 或者 二进制。
1、应用场景:
存储key-value键值对,这个比较简单不细说了
2、字符串(String)常用的命令:
set [key] [value] 给指定key设置值(set 可覆盖老的值)
get [key] 获取指定key 的值
del [key] 删除指定key
exists [key] 判断是否存在指定key
mset [key1] [value1] [key2] [value2] ...... 批量存键值对
mget [key1] [key2] ...... 批量取key
expire [key] [time] 给指定key 设置过期时间 单位秒
setex [key] [time] [value] 等价于 set + expire 命令组合
setnx [key] [value] 如果key不存在则set 创建,否则返回0
incr [key] 如果value为整数 可用 incr命令每次自增1
incrby [key] [number] 使用incrby命令对整数值 进行增加 number
复制代码二、list(列表)
Redis中的list和Java中的LinkedList很像,底层都是一种链表结构, list的插入和删除操作非常快,时间复杂度为 0(1),不像数组结构插入、删除操作需要移动数据。
像归像,但是redis中的list底层可不是一个双向链表那么简单。
当数据量较少的时候它的底层存储结构为一块连续内存,称之为ziplist(压缩列表),它将所有的元素紧挨着一起存储,分配的是一块连续的内存;当数据量较多的时候将会变成quicklist(快速链表)结构。
可单纯的链表也是有缺陷的,链表的前后指针 prev 和 next 会占用较多的内存,会比较浪费空间,而且会加重内存的碎片化。在redis 3.2之后就都改用ziplist+链表的混合结构,称之为 quicklist(快速链表)。
下面具体介绍下两种链表
ziplist(压缩列表)
先看一下ziplist的数据结构,
struct ziplist<T>{
int32 zlbytes; //压缩列表占用字节数
int32 zltail_offset; //最后一个元素距离起始位置的偏移量,用于快速定位到最后一个节点
int16 zllength; //元素个数
T[] entries; //元素内容
int8 zlend; //结束位 0xFF
}
复制代码int32 zlbytes: 压缩列表占用字节数 int32 zltail_offset: 最后一个元素距离起始位置的偏移量,用于快速定位到最后一个节点 int16 zllength:元素个数 T[] entries:元素内容 int8 zlend:结束位 0xFF
压缩列表为了支持双向遍历,所以才会有 ztail_offset 这个字段,用来快速定位到最后一 个元素,然后倒着遍历
以上图片源自网络,如有侵权联系删除
entry的数据结构:
struct entry{
int<var> prevlen; //前一个 entry 的长度
int<var> encoding; //元素类型编码
optional byte[] content; //元素内容
}
复制代码entry它的 prevlen 字段表示前一个 entry 的字节长度,当压缩列表倒着遍历时,需要通过这 个字段来快速定位到下一个元素的位置。
1、应用场景:
由于list它是一个按照插入顺序排序的列表,所以应用场景相对还较多的,例如:
- 消息队列:lpop和rpush(或者反过来,lpush和rpop)能实现队列的功能
- 朋友圈的点赞列表、评论列表、排行榜:lpush命令和lrange命令能实现最新列表的功能,每次通过lpush命令往列表里插入新的元素,然后通过lrange命令读取最新的元素列表。
2、list操作的常用命名:
rpush [key] [value1] [value2] ...... 链表右侧插入
rpop [key] 移除右侧列表头元素,并返回该元素
lpop [key] 移除左侧列表头元素,并返回该元素
llen [key] 返回该列表的元素个数
lrem [key] [count] [value] 删除列表中与value相等的元素,count是删除的个数。 count>0 表示从左侧开始查找,删除count个元素,count<0 表示从右侧开始查找,删除count个相同元素,count=0 表示删除全部相同的元素
(PS: index 代表元素下标,index 可以为负数, index= 表示倒数第一个元素,同理 index=-2 表示倒数第二 个元素。)
lindex [key] [index] 获取list指定下标的元素 (需要遍历,时间复杂度为O(n))
lrange [key] [start_index] [end_index] 获取list 区间内的所有元素 (时间复杂度为 O(n))
ltrim [key] [start_index] [end_index] 保留区间内的元素,其他元素删除(时间复杂度为 O(n))
复制代码三、hash (字典)
Redis 中的 Hash和 Java的 HashMap 更加相似,都是数组+链表的结构,当发生 hash 碰撞时将会把元素追加到链表上,值得注意的是在 Redis 的 Hash 中 value 只能是字符串.
hset books java "Effective java" (integer) 1
hset books golang "concurrency in go" (integer) 1
hget books java "Effective java"
hset user age 17 (integer) 1
hincrby user age 1 #单个 key 可以进行计数 和 incr 命令基本一致 (integer) 18
复制代码Hash 和String都可以用来存储用户信息 ,但不同的是Hash可以对用户信息的每个字段单独存储;String存的是用户全部信息经过序列化后的字符串,如果想要修改某个用户字段必须将用户信息字符串全部查询出来,解析成相应的用户信息对象,修改完后在序列化成字符串存入。而 hash可以只对某个字段修改,从而节约网络流量,不过hash内存占用要大于 String,这是 hash 的缺点。
1、应用场景:
- 购物车:hset [key] [field] [value] 命令, 可以实现以用户Id,商品Id为field,商品数量为value,恰好构成了购物车的3个要素。
- 存储对象:hash类型的(key, field, value)的结构与对象的(对象id, 属性, 值)的结构相似,也可以用来存储对象。
2、hash常用的操作命令:
hset [key] [field] [value] 新建字段信息
hget [key] [field] 获取字段信息
hdel [key] [field] 删除字段
hlen [key] 保存的字段个数
hgetall [key] 获取指定key 字典里的所有字段和值 (字段信息过多,会导致慢查询 慎用:亲身经历 曾经用过这个这个指令导致线上服务故障)
hmset [key] [field1] [value1] [field2] [value2] ...... 批量创建
hincr [key] [field] 对字段值自增
hincrby [key] [field] [number] 对字段值增加number
复制代码四、set(集合)
Redis 中的 set和Java中的HashSet 有些类似,它内部的键值对是无序的、唯一 的。它的内部实现相当于一个特殊的字典,字典中所有的value都是一个值 NULL。当集合中最后一个元素被移除之后,数据结构被自动删除,内存被回收。
1、应用场景:
- 好友、关注、粉丝、感兴趣的人集合: sinter命令可以获得A和B两个用户的共同好友; sismember命令可以判断A是否是B的好友; scard命令可以获取好友数量; 关注时,smove命令可以将B从A的粉丝集合转移到A的好友集合
- 首页展示随机:美团首页有很多推荐商家,但是并不能全部展示,set类型适合存放所有需要展示的内容,而srandmember命令则可以从中随机获取几个。
- 存储某活动中中奖的用户ID ,因为有去重功能,可以保证同一个用户不会中奖两次。
2、set的常用命令:
sadd [key] [value] 向指定key的set中添加元素
smembers [key] 获取指定key 集合中的所有元素
sismember [key] [value] 判断集合中是否存在某个value
scard [key] 获取集合的长度
spop [key] 弹出一个元素
srem [key] [value] 删除指定元素
复制代码五、zset(有序集合)
zset也叫SortedSet一方面它是个 set ,保证了内部 value 的唯一性,另方面它可以给每个 value 赋予一个score,代表这个value的排序权重。它的内部实现用的是一种叫作“跳跃列表”的数据结构。
1、应用场景:
zset 可以用做排行榜,但是和list不同的是zset它能够实现动态的排序,例如: 可以用来存储粉丝列表,value 值是粉丝的用户 ID,score 是关注时间,我们可以对粉丝列表按关注时间进行排序。
zset 还可以用来存储学生的成绩, value 值是学生的 ID, score 是他的考试成绩。 我们对成绩按分数进行排序就可以得到他的名次。
2、zset有序集合的常用操作命令:
zadd [key] [score] [value] 向指定key的集合中增加元素
zrange [key] [start_index] [end_index] 获取下标范围内的元素列表,按score 排序输出
zrevrange [key] [start_index] [end_index] 获取范围内的元素列表 ,按score排序 逆序输出
zcard [key] 获取集合列表的元素个数
zrank [key] [value] 获取元素再集合中的排名
zrangebyscore [key] [score1] [score2] 输出score范围内的元素列表
zrem [key] [value] 删除元素
zscore [key] [value] 获取元素的score
复制代码总结
本文很多概念都一带而过了,只是给大家粗略的讲述一下Redis五种基础数据结构和应用场景,旨在给小伙伴们一个面试备题的方向,后续会持续输出Redis方面的文章,欢迎关注,咱们一起学习拿offer。
作者:程序员内点事
链接:https://juejin.cn/post/6974707629994016798
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
相关推荐
- oracle数据导入导出_oracle数据导入导出工具
-
关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...
- 继续学习Python中的while true/break语句
-
上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个...
- python continue和break的区别_python中break语句和continue语句的区别
-
python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...
- 简单学Python——关键字6——break和continue
-
Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
-
用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...
- Python 中 break 和 continue 傻傻分不清
-
大家好啊,我是大田。...
- python中的流程控制语句:continue、break 和 return使用方法
-
Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...
- L017:continue和break - 教程文案
-
continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...
- 作为前端开发者,你都经历过怎样的面试?
-
已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...
- 面试被问 const 是否不可变?这样回答才显功底
-
作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...
- 2023金九银十必看前端面试题!2w字精品!
-
导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。...
- 前端面试总结_前端面试题整理
-
记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...
- 由浅入深,66条JavaScript面试知识点(七)
-
作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录...
- 2024前端面试真题之—VUE篇_前端面试题vue2020及答案
-
添加图片注释,不超过140字(可选)...
- 今年最常见的前端面试题,你会做几道?
-
在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...
- 一周热门
- 最近发表
-
- oracle数据导入导出_oracle数据导入导出工具
- 继续学习Python中的while true/break语句
- python continue和break的区别_python中break语句和continue语句的区别
- 简单学Python——关键字6——break和continue
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
- Python 中 break 和 continue 傻傻分不清
- python中的流程控制语句:continue、break 和 return使用方法
- L017:continue和break - 教程文案
- 作为前端开发者,你都经历过怎样的面试?
- 面试被问 const 是否不可变?这样回答才显功底
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)
