小白程序员对redis知识点的总结

redis知识点总结

大家好啊,这里是小白程序员的第一篇技术博客,肯定有很多坑和很多的问题,还望大家积极指出不足指出,小弟在这里拜谢了。

前言

因为一些原因,个人博客很久没有更新了,主要还是自己懒惰,以及有一段时间比较颓废,所以学习没有坚持下去,现在因为疫情的原因,突然有了很大的压力,所以准备重新捡起来学习的进程,一方面也是对自己的一个监督,一方面也是对自己学习的一个总结。
因为我报了一个网上的学习班,所以有很多的知识点是老师直接传授的,而我自己写博客就是根据自己学习到的知识进行自我的一个总结,可能描述不是很精确,但是至少应该是比较好理解的,我也会尽可能的描述的通俗易懂不出错,有问题的地方还望大家指出。

redis的特点

一个字,快。我看完了和redis相关的视频后,最大的感受就是redis很快,有多快呢?一个单机的redis就可以支持十几万的并发请求,而且因为redis是基于内存的,他没有数据从磁盘加载到内存的一个过程,RDB和AOF不算 ,所以比起传统的数据库,redis在性能上是有绝对的优势的,但是因为RDB和AOF的存在,redis也可以作为数据库使用,但是我总觉得这个有点本末倒置的感觉。

redis和memcached的区别

这样说,在我的理解里面,redis比memcached多的就是对数据类型的支持。
memcached只支持字符串,而redis支持五大类型,String,list,hash,set,serted_set。
其中,String还可以分为字符串,数值以及重要的bitmap,这个数据类型虽然我没有用过,但是我根据老师的描述觉得这个类型好TM的牛逼,这个等会儿就会说。
可能有些没有深究过得同学会觉得我需要这么多的类型,String走天下,存一个json格式的字符串已经可以解决百分之99的问题了。
不瞒大家说,最开始我也是这样想的,甚至现在我也是这样再用,但是我们不能说这些类型没有啊。
究其原因我个人觉得还是因为我们做的东西数据量太小了,像我公司做的那些并发又低,数据量又小,单台redis用String完全够用了,没必要再去弄这些东西,而且还容易出错。
但是我觉得技术这个东西,能多学点就多学点总没错。下面我给大家举个例子就明白了。
list,这个类型相信大家都不陌生,如果我们用一个json格式的字符串来存一个list,如果list里面只有100条数据可能效果都差不多,但是如果数据量有100W条呢?我们需要从redis中取出这100W条数据,然后去遍历知道我们需要的数据。我们本来只需要加载一条,结果因为这一条我们加载了100W条数据,这个性能的浪费就很吓人了。但是如果你用redis的类型来做,就很方便了,它提供了list这个类型的一些通用方法,你可以根据下表直接去获取,虽然也有这个便利的过程,但是他不需要将这100W的数据从redis中取出啊,这个性能就有很大的说法了。
说到这里大家应该就明白了,redis这些类型不是没用,而是我们根本用不到!!!!!但是面试的时候面试官会问啊QAQ,所以还是学习一下总没错。

redis里面的各种类型

String:

最常见,也是最常用的类型,但是很多同学可能不知道,Redis里面String是分为了三个类型,分别是字符串,数值,bitmap。
字符串:就是我们常见的字符串
数值:就是我们常见的数值,但是有个特点,就是redis里面你如果存的数值那你是可以直接调用方法做运算的!!这玩意儿我当时知道的时候也是惊了。
给大家举个例子,我往redis里面存了一个1,如果我想给这个加一,一般来说我是把这个取出来,然后++,然后再设置回去,但是现在可以直接调用对应的方法,就可以直接运算了,少了一个获取的操作!!
我想大家也体验到了,redis在各个地方都提供了对应的简化方法,要的就是我们将计算逻辑扔到redis里面去,让他来做,我们只需要结果就可以了,减少数据搬运的过程,我想这也是redis快的原因吧。

bitmap

也叫位图,是通过二进制位来表示数据的,这种数据结构通常使用在海量数据,且数据表示状态只有两种的情况,对数据进行统计,例如,要统计张三一年下来登录了多少次,每天只计算一次这种需求,那么我们可以在redis中已张三为key,value最大给他370个位的数据空间,转换成字节也就50个字节左右,每一位代表一年中的每一天,如果登录则设置为1 这样统计的效率将会大大提高,统计其他的东西只需要按照需求将这个矩阵进行对应的旋转调整即可
上面是我做的笔记,我总结一下就是,bitmap通过二进制位来表示一个变量的状态或者值,在上面的例子里面我也写的很详细,如果通过mysql来计算的话耗时不说还费资源,但是用redis的bitmap来做,那就很快了,所以在redis中我们一定要找到每个类型的合理使用场景,这玩意儿虽然你可能不会用,但是面试的时候一定会问!!!

结语

今天就暂时写到这儿了,毕竟晚上了,早点睡觉狗命要紧,在以后的时间里我会努力多更博客,对自己所掌握的知识点做及时的回顾整理,那就这样了,大家明天见~~ 2022-12-27
哦对了,我这次想写的所有的redis的知识点我都会写在这一篇博客上,直接往下更新就完事儿了~~
2022-12-28
兄弟萌,我又来了,继续更新~~~~

list

也就是链表,常见的数据类型,redis中的链表是双向无环的链表,但他是有序的,这里的有序不是说数据有序,而是添加进去的顺序有序。
例如,我添加1,3,5,2,4,那么我取出来也是1,3,5,2,4。顺序是不会乱的 不是说他会排序。
redis所提供的方法分为从链表头和链表尾部操作,如果同向方法 则可以把该链表理解为,如果是反向方法,则可以把链表理解为队列,对于链表本身的操作,其实可以看做是一个数组,且redis中还有一个功能,可以阻塞式的去获取对应的值,那么可以将list看成一个阻塞的单播的队列,因为这个方法是谁先阻塞,到时候有值了谁就先拿到,所以也可以看成是一个FIFO的一个阻塞单播队列
通过上面的笔记可知,redis中的list和java中的没有啥区别 。

hash

hash可以看成java中的map,都是kv的结构。在redis中存储hash类型的数据结构,其实就和在java中创建一个如下对象一样

Map<String,Map<String,Object>> map = new HashMap();
set和sorted_set

set:
没有重复的,无序的列表,这里的无序是指存放的顺序是无序的。
例如 存入 1,3,5,2,4。有可能取出来就是5,2,4,1,3。
set的集合操作相当多,记住常用的在这里插入代码片即可,有需要可以查询文档。
set还有一个功能是取随机个数的值,如果你传入的取得个数为正数,那么该返回多少就返回多少,如果你传入的是一个负数,那么如果超过了长度,他会返回一个包含重复数据的集合。
sorted_set:
一个有序的set集合,因为你在存入这种数据结构的时候,再添加数据的同时,需要添加这个数据的权重也就是score,如果全部为同一个值,则会通过字典表来排序
他的排序速度很快,是为什么?他的底层的数据结构为skiplist 也就是跳跃表或者类平衡树 速度方面他的增删改查全部过一遍的话速度是相对最有的

数据类型结语

到这里,redis的五大数据类型就已经写的差不多了,这些东西是经过我自己总结后写出的,可能会有纰漏或者不准确的地方,也欢迎大家在评论中指出,谢谢。

Redis的其他知识点

管道

不知道大家用过linux没有,在linux系统中查询一个进程,我自己用的语句为

ps -ef | grep tomcat

这条语句是查询进程名称为tomcat的进程,在这条语句中,| 符号就是管道,他将前一条语句的结果当成第二条语句的参数,返回一个符合后一条语句的筛选结果,在redis中,管道的用法也差不多

"xxxx\r\nxxxx\r\nvvvv" | nc localhost 6379

管道这一块我自己没有怎么细看,大差不差是这个意思。

发布订阅

大家应该也用过消息中间件吧,各种mq,其实redis也支持这个功能,它同样可以订阅主题,和发布信息,用法和市面上的mq大同小异,这里就不再详细说明了

事务

因为redis是单进程的,所以就算有多个客户端同时发送,他在内存里面也是依次排序的,在监听到某一个客户端发送exec命令后,redis会将这个客户端所发送的命令全部一股脑的执行,而没有收到exec的客户端的命令则不会执行。

watch

监控,可以监控某一些key,在多个客户端并发操作的时候,如果操作的同一个值,例如 a,在两个客户端都watch了这个key以后,当其中一个客户端修改了这个值并且操作成功了以后,另一个客户端在执行的是否发现这个值被修改了,那么他就不会执行该事务中的代码了,这个时候我们可以通过程序去捕获这个问题然后进行对应的逻辑处理
最开始我也不知道redis居然是支持事务的!!

Redis作为数据库/缓存的区别

缓存数据没有数据库中的数据重要,数据库是全量数据,缓存不是全量数据,缓存应该随着访问变化,常用的查询数据,也叫热数据

redis作为缓存的话,他的数据怎么能随着业务变化,只保留热数据,因为内存大小是有限制的,有瓶颈

那么我们就需要考虑数据的有效期,也就是key的有效期,而推送有效期的主要来源是业务逻辑的

第二,我们淘汰某些key,因为有些数据访问一次后可能就不会访问了,所以我们需要将他从内存中移除

我们可以使用相关算法来筛选某一些可以移除的key

LRU 个人理解是在你两次访问这个key的间隔是多少,间隔越久,就越容易被移除

LFU个人理解是给定一个时间段,这个时间段内,哪些key是被访问的最少的

redis中内置的筛选算法为:

noeviction:新增数据时没有空间直接报错

allkeys-lru:在所有key中找到是用最少的key

volatile-lru:在即将过期的key中找到使用最少的key

allkeys-random:全key随机

volatile-random:过期key随机

volatile-ttl:回收过期key,尤其时那些存活时间较短的key

一般来说我们使用的时allkeys-lru和volatile-lru,如果设置了过期时间的key较多 就用第二种,否则第一种

第三和第四种太随意了,第五种有时间比较成本,第一种是redis作为数据库时使用的

key的有效期

redis设置key的有效期会因为操作了该key而延长有效期嘛?

答案是不会的

但是如果是发生了修改操作,那么修改后的key的有效期将直接变为永久有效

redis如何判断过期两种方式

被动和主动

被动的意思是,一个key过期了,redis不会去清除他,当有客户端访问这个key的时候,redis会判断这个key是否过期,如果过期了则返回客户端没有数据并且清空这个key,这个的缺点是,如果有key长时间没人访问,那就会一直占用内存,这样的key多了,就会导致内存不够用

主动清除就是说,redis每隔一个指定时间去随机获取20个key,然后判断这20个key里过期的key是哪些,然后删除,删除完成后统计下删除个数,如果删除的比例超过百分之25,则又取20个,继续删,直到最终删除比例在25以下

击穿 穿透 雪崩

我相信这三个也是面试的时候经常被问到的词儿,这儿我也自己总结一下供大家参考。
大家可以这样理解,击穿是把redis打穿了,穿透是连着数据库一下全部都打穿了,雪崩简单理解就是大量的击穿合在一起。
可能听着比较抽象,下面我详细说一下这三者的区别和解决方案

击穿

大家可以想象一个场景,你去查询一个key叫OOXX的数据,结果好巧不巧,正好这个key在你访问的那一刹那过期了,那你是不是就不能再redis中获取到了?获取不到咋办,那就只有去数据库嘛。在这里插入图片描述
大家看这个图片,想不想redis被打穿了,这个就叫击穿,那什么是穿透呢?

穿透

同样的场景,唯一不同的是,这次连数据库都没有了!!!
在这里插入图片描述

雪崩

雪崩你可以理解成大量的key失效,导致所有请求都涌向数据库去查询数据,这个玩意儿通常是在程序更新redis数据的时候出现,例如每天的0点更新redis中的数据,那在这段时间里面大量的key失效,如果同时又有大量的请求过来,那就会造成所有请求压向数据库,造成数据库的查询压力,这就叫雪崩

怎么解决呢?

首先大家要知道,上面这三种情况都是建立在高并发的情况下,如果瞬间并发只有一两个,那根本不用担心这个问题。
我们可以把发生这种事情的情况分成两种,来分别想方案来处理,他们分别是key过期和整个系统没有查询的数据
首先key过期,我们可以通过锁的机制来进行解决,具体描述如下
因为redis是单线程的,所以就算有在大量的请求过来,他也是一条一条执行的,那么我们就可以想个方法,就是在查询redis的时候加一把锁,比如sync,那么当第一个请求发现没有数据的时候,他就回去查询数据库,查询到数据库以后,再回来设置进redis,那么当下一个请求过来的时候,redis中是不是就有数据了?那就不会去查询数据了,也就解决了击穿的问题了。
但是这样又会有一个新的问题,如果这个哥们儿去请求数据库的时候失败了咋办,或者挂了,那不就没人来解锁了,就形成了死锁, 所以,redis也给我提供了一个解决方案,就是使用setnx方法。
setnx,当这个值为空的时候,才能设置成功,通过这个方法,我们可以让第一个线程设置一个值,如flag,redis中没有这个值,所以他设置成功,进行下面的操作,在这个时候如果其他线程准备进来设置,调用setnx方法,则会设置失败,因为flag已经有了,就只有等待,等到上一个线程删除这个flag的时候,下一个线程才能设置成功。
这样做的一个好处就是,flag是一个key啊,它可以设置过期时间啊,我们可以设置一个相对合理的过期时间,如果这哥们儿挂了,那到了过期时间,这个flag会自动消除,那么下一个线程就可以成功设置然后进行对应的逻辑了。
那有朋友可能会问,那我要是数据库那边本来就要这样长的时间,结果我还没有处理完,你就给我消了,那不是还是有问题啊。
这个问题嘛,也有解决方案~~
首先,我们设置的超时时间肯定是大部分情况下都可以完美执行程序的,比方说,我的业务要执行10秒,我肯定设置一个11秒或者15秒左右的超时时间,保证我的程序绝对能执行完,那如果确实遇到了15秒都没有执行完的程序怎么办呢?我们可以再跑一个线程,这个线程用来刷新这个key的失效时间。比方说,超时时间是15秒,我的线程再第14秒钟的时候去刷新一次这个超时时间,再让他延迟15秒小时,正常情况下,你再怎么也应该返回了吧,所以我们可以根据业务需求,设置这个线程的刷新次数,比方说延时3次,要是你都没有返回,那我就让他过期算了,因为很有可能这个时候你的线程已经挂了。
在这里插入图片描述
我画了一张图,大概意思是这么个意思。对于key过期,我们可以这样做,那如果整个系统就没有这个数据呢?
这个时候我们就可以用到我们的bitmap了,我们可以将我们预计会放入redis的数据的key做一个运算后得到一个值,这个值对应bitmap中的一个位,当请求过来的时候先去做一次运算得到一个值,然后用这个值去bitmap中找,如果没有找到,那就证明这个值在整个系统中都不存在,那就不用去查询数据库了,减少了大量的无用请求,这也就是布隆过滤器的原理,但是有朋友可能会说,那我要是正巧这个是做了运算后等于一个bitmap中存在的值,但是我这个key本身是查询不到的,因为算法问题导致得到的值冲突了怎么办,这玩意儿没办法,有概率会这样,他不是百分之百能解决的。但他触发这种情况的概率很低,仅有百分之1,但是还有一个问题,布隆过滤器不能删除,这个时候我们就可以换一个更好的过滤器,布谷鸟,他提供了del方法,支持数据的删除

雪崩

上面是对应的击穿和穿透两种情况的解决方案,但是对于雪崩而言,这两种都还不够,因为雪崩是大量的key过期,可能几百万,上千万?这种情况一般来说都是发生在我们能够预计的时间,比如凌晨这种更新数据的时候,那么这个时候,我们就可以让我们的所有请求停顿这么短短的几秒钟,让redis腾出时间来更新数据,当然,我们也可以通过设置随机消失时间,来让key分批次的消失更新,但是这样会导致一个问题,就是有些数据必须是要在0点更新的,你不更新,人用户不就查到老数据了吗?所以这个是根据你的实际业务来处理,没有最好的方案,只有最适合的方案。

Redis的持久化操作

上面讲了这么多,其实还有一个问题我们没有讨论,就是如果redis在运行期间崩了,咋办??
放心,这种情况肯定是有解决方案的,而且大家肯定也很熟悉,那就是持久化,将数据持久化到硬盘,当你redis崩了以后,我重启redis,他直接将硬盘中的数据重新加载到内存中,不就恢复了吗?
redis为我们提供了两种持久化方案,分别叫做RDB和AOF,这两种方案的不同点和优缺点我们一起来讨论下。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值