缓存问题:穿透、击穿、雪崩
缓存问题:穿透、击穿、雪崩
缓存穿透
Pass Through
概念
客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
被动方案
缓存空对象
- 优点:实现简单,维护方便
- 缺点:
- 额外的内存消耗(不过可以通过 TTL 来缓解)
- 可能造成短期的不一致
布隆过滤
优点:内存占用比较少,没有多余 key
缺点:
实现复杂(hash + bitmap + 概率实现的)
存在误判可能(假阳性
说不存在一定不存在,但是说存在的时候也有一定概率是不存在的
接口限流
根据用户或者 IP 对接口进行限流,对于异常频繁的访问行为,还可以采取黑名单机制,例如将异常 IP 列入黑名单。
主动方案
- 增强 id 的复杂度,避免被猜测 id 规律,来渗透数据库
- 做好数据的基础格式校验(如长度,雪花算法)
- 加强用户权限校验
- 做好热点参数的限流 (spring cloud sentinel)
当有大量恶意请求访问不存在的数据的时候,也会发生缓存穿透,因此在 API 入口处我们要判断求请求参数是否合理,请求参数是否含有非法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进一步访问缓存和数据库。
缓存雪崩
概念
同一时段大量的缓存 key 同时失效或者 Redis 服务宕机,导致大量请求到达数据库,带来巨大压力。
解决方案
大量 key 同时过期
给不同的 key 的 TTL 添加随机值
后台更新缓存
业务线程不再负责更新缓存,缓存也不设置有效期,而是让缓存“永久有效”,并将更新缓存的工作交由后台线程定时更新。
事实上,缓存数据不设置有效期,并不是意味着数据一直能在内存里,因为当系统内存紧张的时候,有些缓存数据会被“淘汰”,而在缓存被“淘汰”到下一次后台定时更新缓存的这段时间内,业务线程读取缓存失败就返回空值,业务的视角就以为是数据丢失了。
Redis 故障宕机
利用 Redis 集群提高服务的可用性
服务熔断或请求限流机制是缓存雪崩发生后的应对方案,我们最好通过主从节点的方式构建 Redis 缓存高可靠集群。
如果 Redis 缓存的主节点故障宕机,从节点可以切换成为主节点,继续提供缓存服务,避免了由于 Redis 故障宕机而导致的缓存雪崩问题。
给缓存业务添加降级限流策略
为了减少对业务的影响,我们可以启用请求限流机制,只将少部分请求发送到数据库进行处理,再多的请求就在入口直接拒绝服务,等到 Redis 恢复正常并把缓存预热完后,再解除请求限流的机制。
给业务添加多级缓存
浏览器可以添加缓存(静态数据)
Nginx 缓存
JVM 缓存(本地缓存)
Caffeine
缓存击穿/热点 key 问题
概念
又称热点 key 问题,就是一个被高并发访问并且缓存重建业务较复杂(耗时较长)的 key 突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
可以发现缓存击穿跟缓存雪崩很相似,可以认为缓存击穿是缓存雪崩的一个子集。
解决方案
逻辑过期,推荐!
热点 key 在活动的时候,设计逻辑过期
互斥锁
互斥锁方案,保证同一时间只有一个业务线程更新缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。
缺点:性能比较差,好多别的线程在旁边自旋等待
不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓存以及重新设置过期时间;
针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。
热Key问题可能会带来以下影响:
- 网络拥塞。如果一个Key占用的空间很大,或者请求的命令很复杂,那么每次访问都会消耗大量的网络带宽,可能导致机器或局域网的流量被打满,影响其他服务的通信。
- 响应时间上升、超时阻塞。由于Redis是单线程的,如果一个Key的操作耗时较长,那么就会占用Redis的CPU时间,导致其他请求等待或超时。
- 过期删除阻塞。如果一个Key设置了过期时间,当过期时这个Key会被删除。如果这个Key很大或者很热,那么删除操作可能会阻塞Redis的服务。
- 主从同步中断。如果一个Key很大或者很热,在主库上进行操作可能会造成主从复制的延迟或中断,影响数据的一致性和可用性。
- 缓存穿透。如果一个Key很热,在Redis上失效或者被删除后,那么所有的请求都会直接打到后端数据库上,可能导致数据库压力过大或者崩溃。