目录
1、什么是缓存
2、为什么使用Redis作为MySQL的缓存
3、缓存的更新策略
3.1、策略一:定期生成
3.2、策略二:实时生成
内存淘汰策略【面试重点】
4、缓存预热(Cache preheating)【面试重点】
5、缓存穿透(Cache penetration)【面试重点】
6、缓存雪崩(Cache avalanche)【面试重点】
7、缓存击穿(Cache breakdown)【面试重点】
1、什么是缓存
缓存,其实核心实现的就是能够更快访问到数据~
更快既然是一个比较级的关系,那么就是会有一个相对关系,谁对谁来说更快,谁对谁来说访问速度更快,是谁谁的缓存。
举例理解:你把钱存在银行,每次买东西之前,你需要去银行取钱,然后再来付款,这个过程就会比较麻烦;这种情况下,你可以取一小部分钱放在口袋里,每次买东西付款时直接从口袋里取钱,这个过程就会相对来说更加高效了。此时口袋相对银行来说访问速度更快,就可以使用口袋作为银行的缓存,大大提高办事效率~
那么,对于计算机来说,硬盘相对于网络来说访问速度更快,就可以使用硬盘作为网络的缓存;内存相对于硬盘来说访问速度更快,就可以使用内存作为硬盘的缓存;CPU寄存器相对于内存来说访问速度更快,就可以使用CPU寄存器作为内存的缓存。
那上述所说的缓存是更快了,但并不意味着可以把所有数据都放在缓存中,因为缓存的空间往往是有限的,缓存上只放一些热点数据就可以了~【二八定律】
2、为什么使用Redis作为MySQL的缓存
Redis典型的应用场景就三个:存储数据 、 缓存【最常见】 、 消息队列。
下面就简单介绍一下为什么使用Redis作为MySQL的缓存:
1)MySQL·(关系型数据库)虽然功能强大,但是其性能并不高,每一次查询操作消耗的系统资源比较多。性能不高,其承担的并发量就有限,一旦请求数据量多了,数据库的压力就会很大,甚至很容易宕机了~
2)关系型数据库为什么性能不高:数据库把数据存储在硬盘上,硬盘的IO速度并不快,尤其是随机访问;如果查询不能命中索引,就需要进行表的遍历,这就会大大增加硬盘IO次数;关系型数据库对于SQL的执行会做一系列的解析,校验,优化工作;如果是是一些复杂的查询,比如联合查询,需要进行笛卡尔积操作,效率更是降低很多~
3)提高MySQL能承担的并发量:开源->引入更多的机器,构成数据库集群;节流->引入缓存,把一系列的热点数据保存在缓存上,后续查询数据时,如果缓存中已经存在,就不用再访问MMySQL了~
那我们如何知道Redis中应该存储哪些数据呢?怎么判定他是热点数据呢?具体看我们下面介绍的缓存的更新策略~
3、缓存的更新策略
3.1、策略一:定期生成
将一定时间内访问的数据,以日志的形式记录下来,对于其访问的数据频次进行统计,挑选出访问频次最高的前N%的数据,定期将这些数据同步到缓存服务器上,控制这些缓存服务器自动重启~
通常情况下,会有一个脚本代码来执行这个操作,通过定时任务来触发:
- 完成统计热数据过程
- 根据热数据,找到其查询结果的数据
- 将这些缓存数据同步至缓存服务器上
- 控制这些缓存服务器自动重启~
优点:
上述方式,在实际实现会比较简单,过程更加可控(缓存中有啥数据是比较固定),方便排查问题~
缺点:
实时性不够,如果有突发事件的相关数据变成热数据了,但由于数据还没有同步到缓存服务器中,就会给数据库带来较大的压力~
3.2、策略二:实时生成
实时生成,其过程就是:
- 如果Redis中查到了,就返回其结果
- 如果Redis中不存在,就从数据中查,把查询结果同时也写到Redis中~
但是这种方式也会带来一个问题:Redis中不存在的数据很多,每次查都往里新增数据,不停地增加后,内存可能会达到上限,此时再插入数据,可能就会触发一些问题。此时Redis就引入了“内存淘策略”,如下:
内存淘汰策略【面试重点】
通用的淘汰策略:
- FIFO(First In First Out)先进先出:把缓存中存在时间最久的淘汰掉---->按进入Redis的时间按序,时间越老越先淘汰
- LRU(Least Recently Used)淘汰最久未使用的:记录每个key的最近访问时间,把最近访问时间最老的key淘汰--->按最近访问时间排序,时间越老越先淘汰
- LFU(Least Frequently Used)淘汰访问次数最少的:记录每个key的最近一段时间的访问次数,那访问次数最少的淘汰---->按某段时间内的访问次数排序,次数越少越先淘汰
- Random随机淘汰:在所有的key中随机淘汰~
Redis中的淘汰策略:
- volatile-lru:当内存不足以容纳新写的数据时,从设置了过期时间的key中使用LRU算法进行淘汰
- allkeys-lru:当内存不足以容纳新写的数据时,从所有的key中使用LRU算法进行淘汰
- volatile-lfu:当内存不足以容纳新写的数据时,从设置了过期时间的key中使用LFU算法进行淘汰
- allkeys-lfu:当内存不足以容纳新写的数据时,从所有的key中使用LFU算法进行淘汰
- volatile-random:当内存不足以容纳新写的数据时,从设置了过期时间的key中,随机进行淘汰
- allkeys-random:当内存不足以容纳新写的数据时,从所有的key中随机进行淘汰
- volatile-ttl:当内存不足以容纳新写的数据时,从设置了过期时间的key中,根据过期时间,越早过期的优先被淘汰
- noeviction:默认的策略,当内存不足以容纳新的数据时,新写入的操作会报错~
上述的Redis淘汰策略,其实就是根据通用的策略,再结合Redis的特点【可以设置过期时间】,来进行淘汰数据~
4、缓存预热(Cache preheating)【面试重点】
对于缓存中的数据,如果采用实时生成的策略,就需要考虑到预热问题。
因为在最开始,Redis服务器首次连接,其中还没有数据,那所有的请求就会被打给MySQL了,随着时间的推移,Redis上的数据越积累越多,MySQL承担的压力才会逐渐减小~
如何解决上述问题:
使用缓存预热。具体来说就是把定期生成和实时生成结合一下,先通过离线的方式,通过一些统计的途径,先把热数据找到一批,导入到Redis中,此时这些导入的热点数据就能帮MySQL承担很大的压力了,随着时间的推移,逐渐就使用新的热点数据,淘汰掉旧的数据~
5、缓存穿透(Cache penetration)【面试重点】
什么是缓存穿透?
当在查询某个key时,Redis中没有该数据,然后去MySQL中查询,MySQL里面也没有这个值,那这个key肯定就也不会被更新到Redis中,那后续再查这个key,依然没有这个数据。像这种的数据,如果存在很多,并且还一直被反复查询,一样也会给MySQL带来很大的压力~
为什么会出现这种情况?
- 业务设计不合理。例如缺少了参数校验的环节,导致非法的key也能够被查询了
- 开发/运维操作失误:不小心把部分数据从数据库中误删了
- 黑客恶意攻击
如何解决问题?
如果出现过这种情况,我们在以后的工作中,就会得到教训:要改进业务、加强监控报警系统等~ 那这些都是以后的工作中得到的教训,话说回来,那当你正发现这个问题了,你应当如何降低问题的严重性呢?如下:
- 1:如果发现了这个key,在Redis和MySQL上都不存在,仍然将这个key写入到Redis中,value设成一个非法值(例如 “”)
- 2:还可以引入布隆过滤器,每次查询Redis或MySQL之前都先判定一下key是否在 布隆过滤器上 存在~ (所有的key都插入到布隆过滤器中)
6、缓存雪崩(Cache avalanche)【面试重点】
什么是缓存雪崩?
在短时间内,Redis上大规模的key失效,导致缓存命中率陡然下降,并且MySQL的压力迅速上升,甚至直接宕机~
为什么会出现这种情况?
- Redis直接挂了【Redis宕机或者是Redis集群模式下的大量节点宕机】
- Redis正常工作,但是可能之前短时间内设置了很多的key,并且过期时间都是相同的【通过大规模key过期】
如何解决问题?
- 1:加强监控报警系统,加强Redis集群可用性的保证
- 2:不给key设置过期时间 或者是设置过期时间但是同时会加上一个随机因子避免在同一时刻过期
7、缓存击穿(Cache breakdown)【面试重点】
什么是缓存击穿?
缓存击穿也可以叫做是缓存瘫痪~
就是缓存雪崩的一种特殊情况,这对热点的key【热点数据,访问频率高,影响更大】,突然过期了,导致大量的请求直接访问在数据库上了,甚至引起数据库宕机~
为什么会出现这种情况?
- 业务设计不合理。例如缺少了参数校验的环节,导致非法的key也能够被查询了
- 开发/运维操作失误:不小心把部分数据从数据库中误删了
- 黑客恶意攻击
如何解决问题?
- 1:进行必要的服务降级。例如访问数据库的时候使用分布式锁,限制同时请求数据库的并发数
- 2:不给key设置过期时间 或者是设置过期时间但是同时会加上一个随机因子避免在同一时刻过期
好啦,本期到这里咯,下期见~