目录
1. 什么是缓存?
缓存就是我们可以存放数据的地方, 不过访问缓存比 访问我们一开始存放数据的地方 快.
换存的核心思路就是把一些常用的数据放到触手可及(访问速度更快)的地方, 方便随时读取.
对于硬件的访问速度来说, 一般情况下, CPU 寄存器 > 内存 > 硬盘 > 网络
对于计算机硬件来说, 往往访问速度越快的设备, 成本越高, 存储空间越小, 所以多大部分的时候, 我们在缓存中只放一些热点数据.
关于 "二八定律" :
20% 的热点数据, 能够应对 80% 的访问场景.
因此 只需要把这少量的热点数据缓存起来, 就可以应对大多数场景, 从而在整体上有明显的性能提升.
2. 为什么使用 redis 作为缓存
首先 mysql 这种关系型数据库的访问效率并不高.
- 数据库把数据存储在硬盘上, 硬盘的 IO 速度并不快, 尤其是随机访问
- 如果查询不能命中索引, 就需要进行表的遍历, 这就会大大增加硬盘 IO 次数
- 关系型数据库对于 SQL 的执行会做一系列的解析, 校验, 优化工作.
- 如果是一些复杂查询, 比如联合查询, 需要进行笛卡尔积操作, 效率更是降低很多.
如果访问数据库的并发量比较高, 对于数据库的压力是很大的, 很容易就会是数据库宕机
怎么使数据库宕机的概率变小呢?
- 开源 : 引入更多的机器, 部署更多的数据库实例, 构成数据库集群
- 节流 : 引入缓存, 使用其他的方式保存热点数据, 从而降低直接访问数据库的请求数量
redis 就是一个用来作为数据库缓存的一个常见方案.
3. 缓存的更新策略
3.1 定时生成
每隔一定的周期, 对于访问的数据频次进行统计. 挑选出访问频次最高的前 N% 数据
但是这种做法, 实时性较低, 对于一些突发情况应对的不好, 比如突然爆火的词
3.2 实时生成
先给缓存设定容量上限, 接下来用户每次查询, 如果在 redis 查到了, 就直接返回. 如果在 redis 中不存在, 就从数据库查, 把查到的结果同时也写入 redis. 如果 缓存已经满了, 就触发缓存淘汰策略, 把一些 "相对不热门的" 的数据淘汰.
4. 淘汰策略
淘汰策略 :
- FIFO (先进先出) : 把缓存中存在时间最久的 (也就是先来的数据) 淘汰掉.
- LRU (淘汰醉酒未使用的) : 记录每个 key 的最近访问时间. 把最近访问时间最老的 key 淘汰掉.
- LFU (淘汰访问次数最少的) : 记录每个key最近一段时间的访问次数.把访问次数最少的淘汰掉.
- Random (随机淘汰) : 从所有的 key 中抽取幸运儿被随机淘汰掉.
redis 使用的 淘汰策略 :
- volatile-lru : 当内存不足以容纳新写入数据时, 从设置了过期时间的 key 中使用 LRU (最近最少使用) 算法进行淘汰.
- allkeys-lru : 当内存不足以容纳新写入数据时, 从所有 key 中使用 LRU (最近最少使用) 算法进行淘汰.
- volatile-lfu 4.0版本新增 : 当内存不足以容纳新写入数据时, 在过期的 key 中, 使用 LFU 算法进行删除 key.
- allkeys-lfuu4.0版本新增 : 当内存不足以容纳新写入数据时, 从所有 key 中使用 LFU 算法进行淘汰.
- volatile-random : 当内存不足以容纳新写入数据时, 从设置了过期时间的 key 中, 随机淘汰数据.
- allkeys-randomn : 当内存不足以容纳新写入数据时, 从所有 key 中随机淘汰数据.
- volatile-ttl : 在设置了过期时间的 key 中, 根据过期时间进行淘汰, 越早过期的优先被淘汰(相当于 FIFO, 只不过是局限于过期的 key)
- noeviction : 默认策略, 当内存不足以容纳新写入数据时, 新写入操作会报错.
5. 缓存预热
什么是缓存预热 :
使用 Redis 作为 MySQL 的缓存的时候, 当 Redis 刚刚启动, 或者 Redis 大批 key 失效之后, 此时由于 Redis 自身相当于是空着的, 没啥缓存数据, 那么 MySQL 就可能直接被访问到, 从而造成较大的压力. 因此就需要提前把热点数据准备好, 直接写入到 Redis 中. 使 Redis 可以尽快为 MySQL 撑起保护伞.
热点数据可以基于之前介绍的统计的方式生成即可. 这份热点数据不一定非得那么 "准确", 只要能帮助 MySQL 抵挡大部分请求即可. 随着程序运行的推移, 缓存的热点数据会逐渐自动调整, 来更适应当前情况.
6. 缓存穿透
什么是缓存穿透 :
访问的 key 在 Redis 和 数据库中都不存在. 此时这样的 key 不会被放到缓存上, 后续如果仍然在访问该key, 依然会访问到数据库. 这就会导致数据库承担的请求太多, 压力很大.
产生原因 :
- 业务设计不合理. 比如缺少必要的参数校验环节, 导致非法的 key 也被进行查询了
- 开发/运维误操作. 不小心把部分数据从数据库上误删了.
- 黑客恶意攻击.
如何解决 :
- 针对要查询的参数进行严格的合法性校验. 比如要查询的 key 是用户的手机号, 那么就需要校验当前 key 是否满足一个合法的手机号的格式.
- 针对数据库上也不存在的 key, 也存储到 Redis 中, 比如 value 就随便设成一个"". 避免后续频繁访问数据库.
- 使用布隆过滤器先判定 key 是否存在, 再真正查询. (使用 hash + bitmap 的思想, 用比较少的空间, 判定某个元素是否存在)
7. 缓存雪崩
什么是缓存雪崩 :
短时间内大量的 key 在缓存上失效, 导致数据库压力骤增, 甚至直接宕机. 本来 Redis 是MySQL 的一个护盾, 帮 MySQL 抵挡了很多外部的压力. 一旦护盾突然失效, MySQL 自身承担的压力骤, 就可能直接崩溃.
如何产生 :
- Redis 挂了.
- Redis上的大量的 key 同时过期 (短时间内在 Redis上缓存了大量的 key, 并且设定了相同的过期时间.)
如何解决 :
- 部署高可用的 Redis 集群, 并且完善监控报警体系.
- 不给 key 设置过期时间 或者 设置过期时间的时候添加随机时间因子.
8. 缓存击穿
什么是缓存击穿 :
相当于缓存雪崩的特殊情况. 针对热点key, 突然过期了, 导致大量的请求直接访问到数据库上, 甚至引起数据库岩机.
如何解决 :
- 基于统计的方式发现热点 key, 并设置永不过期.
- 进行必要的服务降级. 例如访问数据库的时候使用分布式锁, 限制同时请求数据库的并发数.