一、Redis秘书的诞生
在Redis诞生之前,我们使用MySQL存储数据并进行访问,随着互联网的发展,数据量越来越多,用户读写请求越来越多,仅仅依靠MySQL这哥们一个人干要干废,而且绝大多数情况下,用户只进行读操作,只重复查询一个东西,不进行写操作,这就导致MySQL浪费了很多时间去进行磁盘I/O(,即硬盘的读写能力),后来有的人就寻思,要不学学CPU,给数据库也加个缓存,如果你要反复查询一个东西,那你先从我秘书Redis问一下。因此Redis诞生。
二、MySQL老板和Redis秘书配合下的数据读写原理
那么,现在有了秘书Redis,我要在应用程序上进行一项数据查询,如果该数据是第一次被查询,则该请求先直接通过MySQL数据库进行查询并将结果返回给我,同时将该数据在秘书Redis那里登记一下,后续如果还需要查询该数据,则先找Redis要。
说到这里,那么以后我们在数据查询过程中,只要是遇到重复查询的,Redis就能解决,遇到新查询的,MySQL只干这一次活,后期再查还是找Redis。
1.Redis支持的数据结构存储类型
包含五种基本类型 String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合),和三种特殊类型 Geo(地理位置)、HyperLogLog(基数统计)、Bitmaps(位图)。
Redis把数据记录在内存中,就不用再执行龟速的I/O操作,因此节省了大部分时间。
2.Redis遇到的麻烦和解决办法
2.1无效数据占用内存和定期删除
功能实现到这里,确实解决了不少麻烦,但是要知道,Redis是将缓存的数据存放在内存中,但内存不是无限量的,如果一直这么无节制使用内存,那内存迟早要GG。
如何解决这个问题呢?答案是:定期删除
我们可以给这些缓存内容设置一个超时时间,具体设置多少时间,让应用程序自己定,Redis只需要将超时数据进行定期删除就可以了。这样大大的减少了没用的缓存数据。但是,内存中的无效数据量也是非常多的,如果秘书Redis只干这一个活,就要全部检查一遍这些数据有没有过期,就严重影响接待新的客户请求,时间紧任务重,Redis只好随机删除一部分数据,反正能解决内存压力就行,先治标再治本。
2.2逃脱随机选择算法的键值和被动式触发删除(惰性删除)
Redis秘书在工作中,为了减少内存占用量,就随便扔一部分数据出去,但是这可能会造成Redis始终有一部分过期数据没有丢掉。(这个其实很好理解,我们上学的时候,做了很多试卷,要么放在桌洞要么放在书包或者储物柜,但是当我们这些地方都满了的时候,新的课本又发下来了,我们没地方放,也没时间去挨个检查存放的这些资料哪些有用,能不能扔,因此,我们只好将记忆中我们能确定的某一部分么用的学习资料扔掉,腾出位置来放新课本。。。真的是懒死了对吧?)
如何解决这个问题?答案是:惰性删除
Redis秘书就想到了这样一个办法,如果再遇到某个数据查询请求,被秘书发现这个数据以前查过但是已经超期了,那么Redis就直接删除这个超期数据,重新进行。这样来讲,Redis秘书好像只有在遇到这个数据并发现它没用了才会丢掉。(真是让你知道你才干活,不告诉你你就不干活!)
2.3逃脱随机选择算法和惰性删除的数据的解决办法(内存淘汰策略)
为什么还没解决!?问题就是,假设我查询了A/B/C/D,秘书给我登记了,几天后,这四个都过期了,A运气不好,被秘书随机删除了,B又被第二次查询时给惰性删除了,但是C,D在后来的工作中,运气非常好,逃避随机删除,而且就被查询过一次,因此就一直存放在内存中。
同时,还有一个新的问题,就算能够把过期数据定时删除,那么万一Redis秘书想上1休3,结果内存2天就满了,等Redis干活的时候,内存都完蛋了(大清都亡了!)
那么,Redis秘书不懒了,憋了个大招-------内存淘汰策略,用于面对内存不足的问题
以下是8种内存淘汰策略:
noeviction:返回错误,不会删除任何键值
allkeys-lru:使用LRU算法删除最近最少使用的键值
volatile-lru:使用LRU算法从设置了过期时间的键集合中删除最近最少使用的键值
allkeys-random:从所有key随机删除
volatile-random:从设置了过期时间的键的集合中随机删除
volatile-ttl:从设置了过期时间的键中删除剩余时间最短的键
colatile-lfu:从设置了过期时间的键中删除使用频率最少的键
allkeys-lfu:从所有键中删除使用频率最少的键
3.MySQL遇到的安全性问题
Redis秘书终于放了大招,以为她的这套组合拳无懈可击,老板终于可以轻松一些,甚至不用上班。但实际上,MySQL并没有这么轻松。他遇到了更多的问题。
3.1.缓存穿透(无效查询)
你Redis秘书这么厉害,我要查东西你就能给,只有找你没查不到的才能接触到你的老板是吧?
好好好你这么整,我听说Redis秘书的登记簿里存的都是英文(打个比方),上次查abandon你说这种小事不用找你老板MySQL,那我这次带来的是一堆汉字,我给你带出师表、滕王阁序、将进酒。。。你先查查有没有,没有是吧?那我去找老板了,这么巧!老板也没有?!。
结果就发现,Redis忙活半天没有找到东西,MySQL也忙活半天没有找到东西,干了3天活,白干,浪费人力物力。由于MySQL也没有这个数据,那么Redis就没有登记。找到这个破绽,我就天天带这类东西让你俩查,累死你俩。(这个过程,就叫缓存穿透)
3.2缓存击穿(热点数据)
有一天,我拿着一个数据来找Redis,先给了Redis一份,Redis发现没有登记,就交给MySQL去进行查找,但是MySQL发现这个数据太大了,MySQL一下懵逼了,问Redis怎么回事,Redis说这次确实查到了,这是一条热点数据,我发现过期了还是定期删掉了。结果就原封不动按照规则给MySQL了。。。。
这个过程就叫缓存击穿,一条数据,就相当于一条细线--------,但是这条数据多了,就像粗线,力量就大,像一道激光一样穿透Redis,直击MySQL。
3.3缓存雪崩
雪崩,大家肯定知道怎么回事。缓存雪崩,就是有n多个缓存击穿这样的热点数据,都过时了被删掉,导致大批量的数据都交给了MySQL进行工作。
那么Redis就想了个办法,让应用程序的过期时间设置的均匀一些,不至于大批量的数据集体失效,因此,后来就把过期时间进行了随机设置,并且设置热点数据永不过期。
3.4Redis自身问题
有一天,Redis工作中不小心出错,导致整个进程出错,等Redis再次启动时,之前缓存数据全没了,所以又从零开始了。因此,如果能让Redis记住崩溃前的缓存内容就好了.....
这,就涉及到了持久化存储机制。
三、持久化存储机制
在Redis遇到这次的bug事件后,想到可以把数据在硬盘上保存一下,不至于崩溃以后重新建立缓存,因此琢磨出了一个RDB持久化存储机制。
1.RDB持久化存储机制
Redis这次不懒了,把缓存内容全部遍历一遍,写入了文件中,为了节约空间,定义一个二进制格式,把数据都连在一起,生成了RDB文件。但是我们知道数据量很大,Redis不能每次光干这个活了,而且有时候直接进行写入操作就行,没有必要一直读取。因此有了下面的配置参数,既可以支持周期性备份,又可以避免做无用功。
save 500 2 #900秒内有2个写入
save 300 10 #300秒内有10个写入
但是这不如创建一个子进程,专门做这个事。
事情好像变得合理了起来,但是有个严重问题我们没有意识到,我们设置的按秒备份,但是每秒钟会同时有n多个请求发送过来,这样不就丢失了很多数据?
这就不得不提二进制日志binlog
2.二进制日志binlog
这个文件里面记录了MySQL对数据执行的所有操作,等需要进行数据恢复的时候就可以用上。
3.AOF持久化存储机制
3.1缓冲区 aof-buf
这是把之前Redis做的所有命令请求记录下来,写入AOF文件,那么该多久写入一次AOF文件呢?Redis建立了一个缓冲区(aof_buf),将命令先存入这里,然后选择时机写入文件。之后,我们可以在对AOF文件中的冗余指令去掉来给它减少大小(这个过程叫AOF重写)但是这个去掉冗余的工作量还是太大了。
因此,可以创建一个子进程干这个事,但是在干这个事情的时候我又新写了东西,就会出现AOF重写数据不一致的问题,那么就再加一个缓冲区aof重写缓冲区(aof_rewrite_buf)
3.2 aof重写缓冲区(aof_rewrite_buf)
从创建子线程开始,再重写缓冲区中把后来写入的命令再写入一份文件,等那边缓冲区子进程结束以后,再把两个文件重命名为一个新文件。
那么RDB到底还要不要了?这个问题下次再写。
内容来源于我之前再某yin看的一个视频,把自己理解的内容按照这种方式记录下来。
这次的笔记记到这里,不足之处敬请批评指正!