Appearance
cache-aside-simulation
1. 实验目标
用纯 Java CLI(无外部依赖)仿真 Cache-Aside 缓存五大经典场景(对应 docs/06-backend-data/07):
- Cache-Aside 读路径:读 Cache → Miss → 读 DB → 写回 Cache
- 缓存穿透:查不存在的 key 绕过缓存直击 DB(布隆过滤器/空值可拦截)
- 缓存击穿:热点 key 过期瞬间并发重建(互斥锁只让一个线程回源)
- 缓存雪崩:批量 key 同时过期 vs 随机 TTL 打散
- 双写一致性:先删缓存后更 DB 的窗口期不一致 vs 先更 DB 后删缓存
2. 工程要点
CACHE(ConcurrentHashMap 模拟 Redis)+DB(HashMap 模拟 MySQL)dbHits计数器量化"穿透到 DB 的次数",直观对比防护效果getWithMutex:Double-Check + 单例锁,50 并发读热点 key 只回源 1 次demoAvalanche:1000 个 key 固定 TTL 全部撞车 vs 随机 TTL 仅 2 个同时过期
3. 运行方式
bash
cd labs/java/backend-data/cache-aside-simulation
javac -d out src/CacheAsideSimulation.java
java -cp out CacheAsideSimulation(无需任何依赖,JDK 8+ 即可)
4. 预期现象
| 场景 | 输出要点 |
|---|---|
| Cache-Aside | user:1 命中缓存,user:2 Miss 后回源并写回 |
| 穿透 | 1000 次查询不存在 key → dbHits = 1000(全量打 DB) |
| 击穿 | 50 并发读热点 key(互斥锁)→ dbHits = 1 |
| 雪崩 | 固定 TTL:1000/1000 同时过期;随机 TTL:2/1000 |
| 双写 | 先删缓存:缓存旧值残留(不一致);先更 DB:缓存 Miss 下次回源新值 |
5. 常见误区
| 误区 | 实际情况 |
|---|---|
| 缓存一定能抗住高并发 | 穿透/击穿/雪崩三类场景都会让流量直达 DB,需要布隆过滤器/互斥锁/随机 TTL |
| 更新缓存比删除缓存好 | 写频繁时更新缓存浪费计算;"删除缓存"懒加载更稳,但要处理窗口期(延迟双删) |
| 击穿和穿透是一回事 | 穿透查的是不存在 key;击穿是单个热点 key 过期瞬间的高并发回源 |