【从零开始学习Redis|第四篇】从底层理解缓存问题:雪崩、击穿、穿透与一致性设计

真正理解雪崩、击穿、穿透的方法只有一个:

理解缓存系统的运行规律

一、为什么需要缓存?

想象你做了一个简单的博客网站。

用户访问文章时,服务器会做这件事:

查询数据库 → 返回数据

如果有 10000 个用户同时访问:

数据库就会变成这样:

大量查询请求 → 数据库 CPU 爆满

数据库是整个系统最贵、最慢的部分。

所以工程师会加一层东西:

缓存

系统结构变成:

用户

服务器

缓存(Redis)

数据库(MySQL)

访问流程是:

1 先查缓存

2 缓存没有再查数据库

3 查到数据后写入缓存

这样大部分请求都会停在缓存层。

数据库压力就会小很多。

二、缓存系统的一个核心特点

缓存不是完整数据

因为缓存通常:

1 有过期时间

2 内存容量有限

所以缓存一定会出现:

有些数据存在 有些数据不存在

正是这个特点,导致了后面三个经典问题:

缓存穿透 缓存击穿 缓存雪崩

它们本质上都是:

缓存没有挡住请求,导致请求直接冲击数据库

只是发生的方式不同。

三、什么是缓存穿透?

想象一个请求:

java 复制代码
/article/999999

但数据库里根本没有这篇文章。

流程会变成:

Redis:没有 MySQL:没有

如果用户不断访问这个不存在的数据:

请求

Redis(没有)

MySQL(没有)

每次都会查询数据库。

这就叫:

缓存穿透

本质是:

查询不存在的数据 缓存无法拦截

穿透为什么危险?

如果有人恶意攻击:

每秒10万请求 访问不存在的ID

那么:

所有请求都会打到数据库 数据库就会崩溃。

如何解决缓存穿透?

1. 缓存空值

当数据库发现数据不存在时:

也写入缓存。

例如:

key: article:999999 value: null

设置短过期时间:5分钟

这样下次访问时:

Redis直接返回null

数据库就不会再被访问。
2. 布隆过滤器

在系统入口建立一个:

布隆过滤器

它的作用是:

快速判断某个ID是否可能存在

如果判断:

一定不存在

就直接拒绝请求。

数据库完全不会被访问。

四、什么是缓存击穿?

缓存击穿发生在:

热点数据

比如一篇特别火的文章:

article:1001

每天可能有:几万次访问

某一刻:

缓存刚好过期。

article:1001 缓存失效

突然:5000个用户同时访问

所有请求都会:

同时查询数据库 数据库瞬间压力暴涨

这就是:

缓存击穿

本质是:

热点数据缓存失效

大量请求同时访问数据库

如何解决缓存击穿?

最常见的方法是:

互斥锁

意思是:

当缓存失效时:

只允许一个线程查询数据库。

流程:

1 第一个请求获得锁

2 查询数据库

3 写入缓存

4 释放锁

其他请求:

等待缓存恢复

这样数据库只会被查询一次。

五、什么是缓存雪崩?

缓存雪崩发生在:

大量缓存同时过期

比如系统中有:

100万个缓存key

如果这些缓存设置了:

同样的过期时间

例如:1小时

那么一小时后:

所有缓存一起失效

此时所有请求都会:

直接访问数据库 数据库瞬间崩溃。

这就是:

缓存雪崩

本质是:

缓存集体失效

数据库压力瞬间爆炸

如何解决缓存雪崩?

  • 方法一:随机过期时间

不要让缓存同时过期。

例如:

3600 + random(300)

意思是:

1小时 + 随机5分钟

这样缓存过期时间就会分散。

  • 方法二:多级缓存

增加缓存层:

本地缓存

Redis缓存

数据库

系统变成:

用户

本地缓存

Redis

MySQL

这样压力会被多层分散。

六、数据库和缓存如何保持一致?

这是缓存系统里最难的问题。

因为系统有两个数据源:

数据库 缓存

如果更新顺序不对,就会出现:

数据不一致

例如:

数据库更新了,但缓存还是旧数据。

常见错误做法

错误流程:

更新数据库

更新缓存

如果更新缓存失败:

数据库新数据

缓存旧数据

就出现不一致。

最常见解决方案:先更新数据库,再删除缓存

工程上最常见的方式是:

更新数据库

删除缓存

流程:

update MySQL

delete Redis

为什么要删除而不是更新?

因为:

缓存可以重新生成

当下次请求到来时:

缓存不存在

→ 查询数据库

→ 重新写缓存

数据自然就一致了。

七、完整流程总结

读取数据:

查Redis

存在 → 返回

不存在

查MySQL

写入Redis

返回

更新数据:

更新MySQL

删除Redis

八、理解缓存系统的核心本质

如果把所有技术细节都抽象掉,缓存系统的本质其实只有一句话:

缓存是用内存空间换取访问时间的一种系统设计。

而缓存问题(雪崩、击穿、穿透)本质都是:

缓存没有成功拦截请求

导致:

请求直接冲击数据库

所以缓存设计的核心目标是:

1 尽量让请求停在缓存层

2 防止请求洪水冲向数据库

3 保证数据最终一致

相关推荐
江湖有缘2 小时前
本地化JSON 处理新方案:基于 Docker的JSON Hero部署全记录
java·docker·json
御坂10101号2 小时前
「2>&1」是什么意思?半个世纪的 Unix 谜题
java·数据库·bash·unix
Java基基2 小时前
Spring让Java慢了30倍,JIT、AOT等让Java比Python快13倍,比C慢17%
java·开发语言·后端·spring
future02102 小时前
Spring AOP核心机制:代理与拦截揭秘
java·开发语言·spring·面试·aop
代码探秘者2 小时前
【Redis】分布式锁深度解析:实现、可重入、主从一致性与强一致方案
java·数据库·redis·分布式·缓存·面试
霖霖总总2 小时前
[Redis小技巧5]Redis Sorted Set 深度解析:从跳表原理到亿级排行榜架构
redis·架构
JAVA学习通3 小时前
InnoDB 存储引擎
java·数据库·mysql
Kim Jackson3 小时前
我的世界Java版1.21.4的Fabric模组开发教程(二十三)创建生物(下)实体在游戏中的实现(1)
java·游戏·fabric