[TOC] #### 1. 前言 --- 你有一个热门商品,每天 10 万人查它,这个商品的缓存设了 1 小时过期 1 小时到了,缓存失效的瞬间,10 万个请求同时发现 Redis 没有,同时冲向 MySQL,MySQL 直接被打爆 这就是缓存击穿,简单来说就是:热点 key 突然过期,大量请求同时冲向 MySQL,导致 MySQL 服务器压力剧增 缓存击穿和缓存穿透的区别: + 缓存穿透是 key 本来就不存在 + 缓存击穿是热点 key 突然过期 #### 2. 方案一:互斥锁 --- 同一时间只让一个请求去查 MySQL,其它请求等着 ```bash set lock:product:2001 1 NX EX 10 ``` 拿到锁的请求去查 MySQL,然后回写 Redis,没拿到锁的请求,稍等一会儿,再重新从 Redis 查询 这个方案的问题是:等待的请求会阻塞,用户体验会稍差 #### 3. 方案二:逻辑过期 --- 不设真正的过期时间,而是在 value 里,额外存一个逻辑过期时间 ```bash hset product:2001 data '{"name":"iPhone"}' expire_at 1715200000 ``` 取数据时,如果发现逻辑过期了:先返回旧数据(用户不会阻塞),然后后台异步更新缓存 用户永远能拿到数据,只是可能短时间拿到旧数据,对大多数业务来说,这种短暂不一致是可以接受的 #### 4. 热点 Key 防护 --- 热点 Key 防护 + 提前缓存:上线前先把热点数据加载到 Redis + 设置更长过期时间:热点 key 不要太容易过期 + 本地缓存:热点数据放到 Caffeine / Guava,连 Redis 都不用查 #### 5. 本文小结 --- 小总结 + 缓存击穿:热点 key 过期,大量请求同时打到 MySQL + 互斥锁:只让一个请求查 DB,其他请求等待 + 逻辑过期:返回旧数据,后台异步更新 + 热点 Key 防护:提前缓存 + 长过期 + 本地缓存