为什么需要缓存?
数据库磁盘 I/O 慢:用户数据一般持久化在关系型数据库中,磁盘读写速度远低于内存。
Redis 缓存层提速:将热点数据放入内存数据库(Redis),读写速度有了很大提升,极大提高系统性能。
其查询流程如下图:
虽然引入缓存后解决了性能问题,但新的问题会随之而来,可能会出现缓存雪崩、缓存击穿、缓存穿透。
缓存雪崩
缓存雪崩指的是当大量缓存 key 在同一时刻失效或 Redis 节点故障宕机,导致原本要将请求打到缓存上的请求全部打到数据库,瞬间压垮关系型数据库,结果引发连锁反应,系统崩溃。其缓存雪崩的可能如下:
大量 key 同时过期(相同 TTL)
Redis 宕机(单点或集群故障)
大量key同时过期
为了缓存和数据库的数据一致性,通常给缓存设置key过期时间。如果一批 key 使用相同或非常接近的过期时间,到期时这些 key 会一起过期。解决方案:
均匀设置过期时间:给TTL加一个随机偏移,如 TTL = base + random(0, spread)
互斥锁:缓存未命中时,只允许一个线程构建缓存,其余等待或返回默认值
后台更新缓存:缓存不设 TTL,由后台线程定时或事件驱动刷新
互斥锁的方案流程图如下:
Redis 宕机
Redis的单节点或集群出现故障,导致缓存层失效,将所有请求直接打到数据库。
服务熔断:检测到 Redis 不可用时,业务直接返回错误,不再访问数据库
请求限流:只允许少量请求访问数据库,其余在入口拒绝
构建高可用集群:Redis 主从 + 哨兵/Cluster,主节点故障自动切换
服务熔断与请求限流的比较如下:
缓存击穿
在平常高并发的系统中,大量的请求同时查询一个 key 时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去。这种现象我们称为缓存击穿。解决方案如下:
互斥锁:同一时刻只允许一个线程重建缓存,其余等待或返回默认值
给热点数据不设置过期时间:热点 key 永不过期,由后台线程定期或在即将过期前主动刷新
缓存击穿和缓存雪崩的区别:
缓存雪崩:大量 key 同时失效
缓存击穿:单个热点 key 失效
缓存穿透
请求的数据既不在缓存中,也不在数据库中,每次请求都会穿过缓存直接查数据库,且无法构建缓存,导致数据库压力剧增。其解决方案如下:
非法请求限制:在 API 入口校验参数合法性,拦截明显非法请求
缓存空值或默认值:当数据库查不到时,在缓存中写一个空值(如 NULL),并设较短 TTL
布隆过滤器:在写入数据库时同步写入布隆过滤器,请求先查布隆过滤器判断是否存在
布隆过滤器在请求链路中的位置
好了,缓存雪崩、缓存击穿、缓存穿透就分享到这里!
一文搞懂缓存雪崩、击穿、穿透,火爆了!