文章目录
- 1.什么是缓存
- 2.缓存更新策略
-
- 2.1定期生成
- [2.2 实时生成](#2.2 实时生成)
- 3.缓存使用注意事项
-
- [3.1缓存预热(cache preheating)](#3.1缓存预热(cache preheating))
- [3.2 缓存穿透(cache penetration)](#3.2 缓存穿透(cache penetration))
- [3.3 缓存雪崩(cache avalanche)](#3.3 缓存雪崩(cache avalanche))
- [3.4 缓存击穿(cache breakdown)](#3.4 缓存击穿(cache breakdown))
- 4.小结
redis的主要用途:
1)存储数据(内存数据库)
2)缓存(redis最常用的场景)
3)消息队列
1.什么是缓存
缓存的本质是 "用更快的存储设备,暂存慢设备的数据,减少慢设备的访问压力"。生活中最常见的例子就是 "浏览器缓存"------ 把你之前访问过的网页图片、JS 文件存在本地硬盘,下次再打开同一网页时,直接从本地读,不用再从远程服务器下载,加载速度快很多。
在后端系统中,缓存的核心价值是解决 "速度不匹配" 的问题:
- MySQL 等数据库数据存在硬盘,读写速度约 100-1000 次 / 秒;
- Redis 数据存在内存,读写速度约 10 万 - 100 万次 / 秒;
- 当用户请求量达到每秒几万次时,直接访问 MySQL 会瞬间把数据库压垮,而 Redis 能轻松扛住。
所以,Redis 作为 MySQL 的缓存,工作流程很简单:
- 客户端查询数据时,先去 Redis 查;
- Redis 有数据(命中):直接返回,不碰 MySQL;
- Redis 没数据(未命中):去 MySQL 查,查到后把数据存到 Redis,再返回给客户端。
2.缓存更新策略
2.1定期生成
把访问的数据,以日志的形式存储起来,每隔一定的周期(一天/一周/一月)对数据进行统计,选出频次高的数据作为缓存。
比如搜索引擎,就是关注"查询词",通过日志,每天统计一整天每个词出现的频率,根据频率取出前20%的词作为"热点词",放入缓存中。
可以写一套离线的流程来完成(shell/python 写脚本代码)
- a)完成热点词的统计
- b)根据热点词,搜索到对应的数据
- c) 把缓存数据同步到缓存服务器上
- d)控制缓存服务器自动重启
优点:实现简单,过程可控,方便排查问题
缺点:缺乏实时性,如果突发热点事件,突然出现很多新的热词,可能就会给后面的数据库带来较大的压力。(缓存里还没有这些热词,会直接在数据库中搜索)
2.2 实时生成
查询数据时,先在redis中查询,找到直接返回。
redis中没有,去数据库中找,找到了返回并把结果写入缓存。
这样的话缓存中的数据就会越来越多---》就会引发内存淘汰策略
- FIFO(First In First Out,先进先出):淘汰缓存中存在时间最久(最先进入缓存)的数据。
- LRU(Least Recently Used,最近最少使用):记录每个 key 的最近访问时间,淘汰最近访问时间最久的 key。
- LFU(Least Frequently Used,最不经常使用):记录每个 key 最近一段时间的访问次数,淘汰访问次数最少的 key。
- Random(随机淘汰):从所有的 key 中随机抽取并淘汰。
redis中有一个配置项,可以设置采取哪种淘汰策略。具体设置哪种还是具体情况具体分析。
- volatile-lru:内存不足时,从设置了过期时间的 key 中用 LRU(最近最少使用)算法淘汰。
- allkeys-lru:内存不足时,从所有 key 中用 LRU 算法淘汰。
- volatile-lfu(4.0 版本新增):内存不足时,从设置了过期时间的 key 中用 LFU 算法删除。
- allkeys-lfu(4.0 版本新增):内存不足时,从所有 key 中用 LFU 算法淘汰。
- volatile-random:内存不足时,从设置了过期时间的 key 中随机淘汰。
- allkeys-random:内存不足时,从所有 key 中随机淘汰。
- volatile-ttl:在设置了过期时间的 key 中,按过期时间淘汰,越早过期越优先。
- noeviction(默认策略):内存不足时,新写入操作会报错,且不适合实时更新缓存场景。
3.缓存使用注意事项
3.1缓存预热(cache preheating)
缓存中的数据
1)定期生成(不涉及"预热")
2)实时生成
redis服务器首次接入后,缓存中没有数据,客户端查redis,没有--》继续查MySQL,查到后返回并写入缓存。
一开始缓存中没有数据,数据库的请求就会很多,随着redis上数据积累,MySQL的压力就小了。
措施:
结合定期生成 和 实时生成。先统计一下数据,把热点数据提前导入缓存,减少数据库的压力。--》随时间推移,逐渐新的热点数据就代替旧的热点数据了。
3.2 缓存穿透(cache penetration)
如果某个key,在redis上没有,在数据库中也没有,每次查询时都会访问到数据库,给MySQL带来很大的压力。
措施:
1)如果发现这个key在redis上和MySQL上都不存在,就写入到redis中,value值设置为一个非法值(比如" ")
2)引入布隆过滤器,每次查询时,提前判断存不存在。
3.3 缓存雪崩(cache avalanche)
短时间内,redis上大规模的key失效,导致命中率陡然下降,进而MySQL的压力迅速上升,甚至宕机。
1)redis直接挂了(redis宕机/redis集群中大量节点宕机)
2)redis在运行,可能短时间内设置了很多带过期时间的key,然后这批key同时过期了。(大量key同时过期)---》导致大量访问传送到了数据库上。
解决措施:
1)加强监控报警,加强redis集群可用性的保证
2)不给key设置过期时间/设置过期时间时避免同一时刻
3.4 缓存击穿(cache breakdown)
热点key突然过期,导致大量请求直接访问到数据库上,甚至引起数据库宕机。
解决措施:
1)基于统计方法发现热点key,设置永不过期
2)进行必要的服务降级,使用分布式锁,限制同时请求数据库的并发数。
分布式锁---》限制数据库访问频率
服务降级---》保留核心,关闭一些不重要的功能
4.小结
- 选对更新策略:数据固定用 "定期生成",数据多变用 "实时生成 + 内存淘汰";
- 必做缓存预热:冷启动前导入热点数据,避免 MySQL 承压;
- 防穿透:用 "空值缓存" 或 "布隆过滤器" 拦截无效请求;
- 防雪崩:错开过期时间,用 Redis 集群提高可用性;
- 防击穿:热点 key 永不过期,或用分布式锁限制并发。