缓存击穿
某一个热点数据失效的一瞬间,大量请求同时访问数据库。
什么是热点数据?
例如:
商品 1001 是一个秒杀商品,每天要承受100万次访问。
Redis goods:1001 缓存 30分钟
正常情况
用户
↓
Redis
↓
返回数据库几乎没压力。
但,当某一时刻 goods:1001 过期了 TTL = 0 这时 10000个请求同时到了。
流程:
请求1 → Redis没有
请求2 → Redis没有
请求3 → Redis没有
...
请求10000 → Redis没有于是这10000个请求都访问了数据库,数据库瞬间压力暴涨。
这就是
缓存击穿
为什么叫击穿?
正常:
请求
↓
Redis
↓
数据库Redis 像一堵墙。而热点 Key 过期后:
大量请求
↓
穿透这一点
↓
数据库就像是墙上被打了一个洞。
举个栗子
腾讯云特价服务器每天 10 点有秒杀。
如果这个服务器的Redis正好在十点失效了,而这时大量的请求同时发送,结果就是数据库瞬间访问暴涨,甚至可能挂掉。
解决方案
- 互斥锁
- 分布式锁
- 逻辑过期
- 热点数据永不过期
方案1:互斥锁(最常见)
流程:
Redis没有
↓
抢锁只有一个线程成功获取到锁,其他线程阻塞等待。
假如 线程A 抢到了锁,那么它接下来:
查数据库
↓
写Redis
↓
释放锁其他线程等待,直到 线程A 释放锁后,他们会重查 Redis。
流程:
10000个请求
↓
Redis没有
↓
竞争锁
↓
A线程获得锁
↓
查数据库
↓
写Redis
↓
释放锁
其它9999线程
↓
重新查Redis
↓
直接返回在这个流程下,数据库只查了1次。
JAVA示例
java
synchronized (key.intern()) {
User user = redis.get(key);
if (user != null) {
return user;
}
user = mapper.selectById(id);
redis.set(key, user);
return user;
}逻辑过期
一般大型系统可能会用
缓存:
json
{
"data": {...},
"expireTime":"2026-06-24 12:00:00"
}即使过期也先返回旧数据,用户得到旧数据,后台线程更新缓存。
优点:不会大量请求数据库
缺点:短时间数据不一致