东营区建设局网站做网站怎么选服务器
什么是缓存
redis 最常用的场景
核心思路就是把一些常用的数据,放到触手可及(访问速度更快)的地方
- ⽐如我需要去⾼铁站坐⾼铁. 我们知道坐⾼铁是需要反复刷⾝份证的 (进⼊⾼铁站, 检票, 上⻋,乘⻋过程中, 出站…)
- 正常来说, 我的⾝份证是放在⽪箱⾥的(⽪箱的存储空间⼤, ⾜够能装). 但是每次刷⾝份证都需要开⼀次⽪箱找⾝份证, 就⾮常不⽅便.
- 因此我就可以把⾝份证先放到⾐服⼝袋⾥. ⼝袋虽然空间⼩, 但是访问速度⽐⽪箱快很多.
- 这样的话每次刷⾝份证我只需要从⼝袋⾥掏⾝份证就⾏了, 就不必开⽪箱了.
- 此时 “⼝袋” 就是 “⽪箱” 的缓存. 使⽤缓存能够⼤ 提⾼访问效率
速度快的设备,可以作为速度慢的设备的缓存
CPU 寄存器 > 内存 > 硬盘 > 网络
最常见的是,使用内存作为硬盘的缓存(redis 定位)
硬盘也可以作为网络的缓存——浏览器的缓存
- 浏览器通过
http/https从服务器上获取到数据(html、css、js、图片、视频、音频、字体…)并进行展示
- 从服务器上获取:网络
- 图片、音频、视频等等这样体积大,又不太会改变的数据,就可以保存到浏览器本地(浏览器所在主机的硬盘上),后续再打开这个页面,就不必重新从网络获取上述数据了
缓存能够有意义,二八定律
- 缓存虽然快,但是空间小
20%的数据,可以应对80%的请求
使用 redis 作为缓存
通常是使用 redis 作为数据库的缓存(MySQL)
- 数据库是一个非常重要的组件,绝大部分商业项目中都会涉及到
- 并且
MySQL的速度又比较慢
为什么说关系型数据库性能不高?
- 数据库把数据存储在硬盘上,硬盘的 IO 速度并不快,尤其是随机访问
- 如果查询不能命中索引,就需要进行表的遍历,这就会大大增加硬盘 IO 次数
- 关系型数据库对于
SQL的执行会做一系列的解析,校验,优化工作 - 如果是一些复杂查询,比如联合查询,需要进行笛卡尔积操作,效率更是降低很多
- …
因为 MySQL 等数据库,效率比较低,多以承担的并发量就有限,一旦请求数多了,数据库的压力就会很大,甚至很容易就宕机了
- 服务器每次处理一个请求,一定都要消耗一些硬件资源(CPU、内存、硬盘、网络…)任意一种资源的消耗超出了机器能提供的上限,机器就很容易出现故障了
如何提高 MySQL 能承担的并发量?(客观需求)
- 开源:引入更多的机器,构成数据库集群
- 节流:引入缓存,就是典型的方案,把一些频繁读取的数据,保存到缓存上。后续在查询数据的时候,如果缓存中已经存在了,就不再访问
MySQL了

- 虽然
redis上只能存少数数据,但是大部分请求都是使用的这少数的热点数据 - 实际业务中,
3:7,1:9无所谓,基本思想是一致的
缓存更新策略
如何知道 redis 中应该存储哪些数据?
如何知道哪些数据是热点数据呢?
这里就要谈到“缓存的更新策略“
1. 定期生成
会把访问的数据,以日志的形式记录下来
比如一个搜索引擎,“查询词”就是要关注的“访问的数据”
- 我们可以通过日志,把搜索框里面都用到了哪些词,给记录下来,就可以针对这些日志进行统计了
- 统计这一天,每个词出现的频率,再根据频率降序来排序,再取出前
20%的词,就可以把这些词认为是“热点词”- 数据量可能大到说,一台机器存不下,就需要使用分布式的系统来存储这些日志(HDFS)
- 再使用
Hadoop的map-reduce来写代码进行统计;也可以基于HDFS的HBASE这样的数据库来写SQL统计 - 这些就是大数据工程师的日常工作
- 接下来就可以把这些热点词,涉及到的搜索结果,提前拎出来,就可以放到类似于“
redis”这样的缓存中了
此处的数据,就可以根据当前这里统计的未读,来定期更新
- 按照天级别统计,就每天更新一次
- 按照月级别统计,就每个月更新一次
写一套离线的流程(往往使用shell,Python写脚本代码),可以通过定时任务来触发
- 完成统计热词过程
- 根据热词,找到搜索结果的数据(广告数据)
- 把得到缓存数据同步到缓存服务器上
- 控制这些缓存服务器自动启动
优点:上述过程,实际上实现起来比较简单,过程更可控(缓存中有什么是比较固定的),方便排查问题
缺点:实时性不够。如果出现一些突发性时间,有一些本来不是热词的内容,成了热词了,新的热词就可能会给后面的数据库什么的带来比较大的压力
- 比如,过年的时候,“春节晚会”就是突发性的热词
2. 实时生成
- 如果在
redis查到了,就直接返回 - 如果
redis中不存在,就从数据库查,把查到的结果同时也写入redis
这样不停地写 redis,就会使 redis 的内存占用越来越多,就会逐渐达到内存上限
- 不一定是机器内存上限,
redis中也可以配置,最多使用多少内存(maxmermory参数)
如果继续往里插入数据,就会触发问题,为了解决上述问题,redsi 就引入了“内存淘汰策略”
内存淘汰策略
#高频面试
先关策略
FIFO(First In First Out)先进先出
把缓存中存在时间最久的(也就是先来的数据)淘汰掉
LRU (Least Recently Used) 淘汰最久未使用的
记录每个 key 的最近访问时间,把最近访问时间最老的 key 都淘汰掉
LFU(Least Frequently Used)淘汰访问次数最少的
记录每个 key 最近一段时间的访问次数,把访问次数最少的淘汰掉
Random 随机淘汰
从所有的 key 中抽取幸运儿被随机淘汰
理解上述几种淘汰策略:
想象你是个皇帝,有后宫佳丽三千。虽然你是真龙天子,但是经常宠信的妃子也就那么寥寥数人(后宫佳丽散散,相当于数据库中的全量数据,经常宠信的妃子相当于热点数据,是放在缓存中的)
今年选秀的一批新的小主,其中一个被你看上了,宠信新人,自然就需要有旧人被冷落,到底谁是要被冷落的人呢?
FIFO:皇后是最先受宠的,现在已经年老色衰了==>皇后失宠LRU:统计最近宠幸时间,皇后(一周前),熹妃(昨天),安答应(两周前),华妃(一个月前)==>华妃失宠LFU:统计最近一个月的宠幸次数,皇后(3 次),熹妃(15 次),安答应(1 次),华妃(10 次)==>安答应失宠(最合理)Random:随机挑选一个妃子失宠(最不合理)
具体采取哪种策略,要结合实际场景来具体问题具体分析
相关配置项
redis 里面,有一个配置项,就可以设置 redis 采取上述哪种策略淘汰内存数据
-
volatile-lru当内存不⾜以容纳新写⼊数据时,从设置了过期时间的key中使⽤LRU(最近最少使⽤)算法进⾏淘汰- 设置了过期时间的就算,包括过期时间还没到的
-
allkeys-lru当内存不⾜以容纳新写⼊数据时,从所有key中使⽤LRU(最近最少使⽤)算法进⾏淘汰. -
volatile-lfu4.0版本新增,当内存不⾜以容纳新写⼊数据时,在过期的key中,使⽤LFU算法进⾏删除key. -
allkeys-lfu4.0版本新增,当内存不⾜以容纳新写⼊数据时,从所有key中使⽤LFU算法进⾏淘汰. -
volatile-random当内存不⾜以容纳新写⼊数据时,从设置了过期时间的key中,随机淘汰数据. -
allkeys-random当内存不⾜以容纳新写⼊数据时,从所有key中随机淘汰数据. -
volatile-ttl在设置了过期时间的key中,根据过期时间进⾏淘汰,越早过期的优先被淘汰.(相当于FIFO, 只不过是局限于过期的key)- 对于其他没有设置过期时间的
key,很可能是没有保存设置时间的
- 对于其他没有设置过期时间的
-
noeviction默认策略,当内存不⾜以容纳新写⼊数据时,新写⼊操作会报错.- 不适合于实时更新策略
经过一段时间的“动态平衡”,redis 中的 key 就逐渐都成了热点数据了
缓存预热(Cache preheating)
#高频面试
缓存中的数据
- 定期生成(这种情况,不涉及“预热”)
- 实时生成
redis 服务器首次接入之后,服务器里面是没有数据的
- 客户端先查询
redis,如果没有查到,就再查一次MySQL,查到了之后,会把数据也写到redis中 - 服务器里没有数据,此时所有的请求都会打给
MySQL,随着时间的推移,redis上的数据越积累越多,MySQL承担的压力就逐渐减小了
缓存预热,就是用来解决上面问题的
把定期生成和实时生成结合一下。先通过离线的方式,通过一些统计的途径,先把热点数据找到一批,导入到 redis 中。此时导入的这批热点数据,就能帮 MySQL 承担很大的压力了。随着时间推移,逐渐就使用新的热点数据淘汰掉旧的数据
缓存穿透(Cache penetration)
#高频面试
查询的某个 key,在 redis 中没有,MySQL 中也没有,这个 key 肯定也不会被更新到 redis 中
- 这次查询:没有;下次查询:仍然没有…
- 如果像这样的数据,存在很多,并且还反复查询, 一样也会给
MySQL带来很大的压力
为何产生?
- 业务涉及不合理。比如缺少必要的参数校验环节,导致非法的
key也被进行查询了- 典型情况
- 开发/运维误操作。不小心把部分数据从数据库上误删了
- 没那么典型,表现也是缓存穿透。误删操作,不一定能及时发现
- 黑客恶意攻击
- 比较少见。黑客是很多的,一些中大厂,都有专门负责做安全的团队(有人负重前行)
如何解决?
通过改进业务/加强监控报警等方式,都是亡羊补牢
相比之下更靠谱的方案:(降低问题的严重性**)
- 如果发现这个
key在redis和MySQL上都不存在,仍然写入redis中,value设成一个非法值(比如"") - 还可以引入布隆过滤器。每次查询
redis/MySQL之前,都先判定一下key是否在布隆过滤器上存在(把所有的key都插入到布隆过滤器中)- 布隆过滤器,本质上是结合了
hash+bitmap,以比较小的空间开销,比较快的时间速度,实现针对key是否存在的判定
- 布隆过滤器,本质上是结合了
缓存雪崩(Cache avalanche)
#高频面试
由于在短时间内,redis 上大规模的 key 失效,导致缓存命中率陡然下降,并且 MySQL 的压力迅速上升,甚至直接宕机
redis直接挂了redis宕机/redis集群模式下,大量节点宕机
redis好着呢,但是可能之前短时间内设置了很多key给redis,并且设置的过期时间是相同的- 给
redis里设置key作为缓存的时候,有的时候为了考虑缓存的时效性,就会设置过期时间(和redis内存淘汰机制,是配合使用的)
- 给
如何解决?
- 加强监控报警,加强
redis集群可用性的保证 - 不给
key设置过期时间/过期时间的时候添加随机的因子(避免同一时刻过期)
缓存击穿(Cache breakdown)
#高频面试
相当于缓存雪崩的特殊情况。针对热点 key,突然过期了,导致大量的请求直接访问到数据库上,甚至引起数据库宕机
- 热点
key访问频率高,影响更大
如何解决?
- 基于统计的方式发现热点
key,并设置永不过期- 往往需要服务器结构做出较大的调整
- 进行必要的服务降级。例如访问数据库的时候用分布式锁,限制同时请求数据库的并发数
- 本身服务器的功能有十个,但是在特定的情况下,适当的关闭一些不重要的功能,只保留核心功能(服务降级)==>手机的省点模式
- 通过分布式锁,限制数据库的访问频率
总结
- 缓存的基本概念
- 如何使用
redis作为缓存 - 缓存更新策略==>
redis内存淘汰机制 - 缓存使用的注意事项
