1.什么是缓存?
把一些常用的数据放到触手可得的地方,方便随时读取.
缓存是计算机中的一个典型应用场景,很多场景中都会涉及.
硬件的访问速度: cpu寄存器 > 内存 > 硬盘 > 网络.
对硬件来说,内存相对于硬盘来说,就是触手可及的地方.内存就是硬盘的缓存;对内存来说,cpu寄存器就是触手可及的地方,cpu寄存器就是内存的缓存.
内存虽然访问速度快,但是他的存储空间小,只能存放一些热点数据.
由于"二八定律" : 20%的热点数据可以处理80%的应用场景 .
因此,只需要将少量的热点数据寸放大缓存里,在整体上就能有明显的提升.
2.使用redis作为缓存
我们经常使用关系型数据库(如 mysql)来存储数据. 关系型数据库虽然功能强大,但存在一个很大的缺陷,就是性能不高.进行一次查询需要消耗非常高的系统资源.
关系型数据库查询效率低的原因:
1. 关系型数据库的数据是存储在磁盘上的,磁盘的IO速度比较慢,特别是随机访问.
2. 当查询不能命中索引时,就需要遍历全表,这就会大大增加磁盘的IO次数.
3. 关系型数据库对于sql执行会做出一系列的解析优化工作.
4. 针对一些复杂查询,进行笛卡尔积,效率会更低.
当访问数据库的并发量很高的情况下, 对数据库的压力会很大,很容易使数据库服务宕机.
提高数据库并发量有两个思路:
1. 开源: 使用更多的机器,部署更多的数据库实例.(主从复制,分库分表)
2. 节流: 引入缓存,使用其他方式保存经常访问的热点数据, 降低直接访问数据库的请求量.
redis就是一个用来作为数据库缓存的常见方案.
redis的数据存放在内存中,读取数据速度快,处理同一个请求消耗的系统资源比mysql少,因此,redis能支持更大的并发量.
每次查询先在redis上查询,查不到时,再想mysql上查询.
3.缓存更新策略
要在redis上存储一些热点数据,又如何知道哪些数据时热点数据呢?
这里有两种缓存更新策略:
1.定期更新:
每隔一定的周期,对访问的数据频次进行统计,挑选出前N%的数据,写一套离线流程,通过定期触发更新redis数据库上的数据.
优点: 实施简单,过程可控,方便排除问题.
缺点: 实时性不够,当突发一些突发事件,有些不是热点词的数据变成热点词了,此时还没来得及更新redis数据库,新的热点词就会给数据库带来较大的压力.
2.实时更新:
查询数据时,先在redis中查找,找到了就直接返回;查不到就去msql中查找,把查询的结果返回并写到redis中.
这种实时更新策略能随时更新redis数据库中的数,但是随着向redis中不断的写入,就会使redis内存占用越来越多.当内存达到上限时,就会出现问题.
为了解决上述问题,redis引入了"内存淘汰策略"来处理:
内存淘汰策略:
1.FIFO(first in first out):
先进先出.把缓存中存放时间最久的数据淘汰.
2.LRU(least recently used):
淘汰最久未使用的: 记录每个key的最近访问时间,淘汰访问时间最老的数据
3.LFU(least frequently used) :
淘汰访问次数最少的, 记录每个key最近一段时间内的访问次数,淘汰访问次数最少的key
4.Random:
随机淘汰, 从所有的key中随机抽取一个数据淘汰.
这些淘汰策略,redis也提供了内置的淘汰策略,可以直接使用:
1>.volatile-lru: 当内存不足的时候,从设置了过期时间的key中,使用LRU(最近最少使用)策略进行淘汰.
2>. allkeys-lru: 内存不足时,从所有key中使用LRU(最近最少使用)策略进行淘汰.
3>.volatile-lfu: 当内存不足的时候,从所有过期的key中,使用LFU(最近最少访问次数)算法进行淘汰.
4> allkeys-lfu: 当内存不足的时候,从所有的key中,使用LFU(最近访问次数最少)算法进行淘汰.
5>. volatile-random: 当内存不足时,从设置了过期时间的key中,随机淘汰.
6>.allkeys-random: 当内存不足时,从所有的key中随机淘汰.
7>.volatile-ttl: 在设置了过期时间的key中,根据过期时间进行淘汰.越早过期的优先被淘汰.(FIFO)
8>. noeviction: 默认策略,当内存不足时,写入新数据会报错.(这个是默认选项,不适用于实时更新缓存).
关于redis缓存存在几个问题:
1.缓存预热Cache preheating:
缓存预热问题是在实时更新的时候出现的.定期更新不涉及缓存预热.
采用实时更新策略时,在刚开始的时候,redis中没有数据,所有的查询都要向mysql访问.导致mysql刚开始的压力较大.
可以采用缓存预热: 通过离线的方式,先统计一波热点数据,导入到redis中,导入的热点数据能帮mysql承担很大的压力.随着时间的推移,逐渐使用新的热点数据淘汰旧的数据.
2.缓存穿透Cache penetration:
查询某个数据key时,在redis中未查到,在mysql中也未查到,就无法更新到redis中,那么这个key在下次查,还是查不到.当存在很多这样的key,并且反复查询,一样会给mysql带来很大的压力.
出现这样的情况的原因:
1. 业务设计部合理,缺少必要的参数校验,导致非法的key也被进行查询了.
2. 误操作导致数据被删除.
3.黑客的恶意攻击.
解决方法:
1. 当发现key在redis和mysql上都不存在时,也将其写到redis上,为器设置一个非法的value.
2. 引入"布隆过滤器" , 每次在查询redis和mysql之前,都先判定一下key是否在布隆过滤器上存在.
把所有的key都存放到布隆过滤器上,布隆过滤器的本质是结合了hash和bitmap,使用较小的空间开销,较快的查询速度,针对key是否存在对出判定.
3. 缓存雪崩Cache avalanche:
在较短的时间内,redis上大规模的key失效,导致redis命中率下降,mysql命中率迅速上升,甚至宕机.
出现的原因:
1. redis挂了.
2.redis上大量的key同时过期.
在往redis上存储key的时候,很多时候,会为其设置过期时间,为大量的key设置了相同的过期时间,当同一时间大量的key同时失效.导致redis的命中率下降.出现缓存雪崩.
解决方案:
1 . 部署高可用的redis集群,完善监控报警系统.
2. 不给key设置过期时间,或设置过期时间时,添加随机时间因子.
4.缓存击穿Cache breakdown:
缓存击穿,是缓存雪崩的一种特殊情况,是针对热点key,突然过期了,导致密集的访问都直接通过mysql查找,导致数据库宕机.
解决方案:
1 . 对热点key,设置永不过时期.
2. 进行必要的服务降级.例如:访问数据库的时候使用分布式锁; 限制同时访问数据库的并发数.