【实战源码】生产级通用缓存安全工具类:彻底解决缓存穿透、击穿、雪崩(附完整流程图 + 源码解析)
·
一、前言
在高并发项目(如 12306 购票、秒杀、海量查询场景)中,Redis 缓存使用不当,极易出现三大经典问题:
- 缓存穿透:查询不存在的数据,绕过缓存直接打数据库;
- 缓存击穿:热点 Key 过期瞬间,大量并发请求直连 DB;
- 缓存雪崩:大量 Key 同时过期,数据库压力骤增。
普通缓存 get -> 判断 -> 查库 简易写法,完全无法应对线上高并发流量。本文基于 12306 开源项目生产级 safeGet 安全缓存查询方法,完整拆解「布隆过滤器 + 分布式锁 + 双重检查 + 自动回填」的万能缓存方案,附带执行流程图、核心原理、适用场景,可直接复用至项目。
二、核心设计思想
该方法是通用泛型缓存查询工具,整合多层防护体系,分层拦截风险请求:
- 第一层:直接查询 Redis 缓存,命中直接返回,保证高性能;
- 第二层:布隆过滤器 + 自定义过滤器,拦截非法空 Key,杜绝缓存穿透;
- 第三层:缓存未命中时加分布式锁,限制单线程查库,防止缓存击穿;
- 第四层:DCL 双重检查,避免重复查库、重复回填缓存;
- 第五层:数据库查询 + 缓存自动回填,兜底业务数据;
- 第六层:空数据自定义回调,完善空值业务处理。
三、完整执行流程图
flowchart TD
A["调用safeGet方法"] --> B["查询Redis缓存"]
B --> C{缓存是否命中?}
C -- 是 --> Z["直接返回结果"]
C -- 否 --> D{布隆过滤器判定Key不存在?<br/>自定义过滤规则拦截?}
D -- 是 --> Z
D -- 否 --> E["加分布式锁Redisson Lock"]
E --> F["二次查询缓存(DCL双重检查)"]
F --> G{缓存是否存在?}
G -- 是 --> H["释放锁"] --> Z
G -- 否 --> I["执行cacheLoader查询数据库"]
I --> J["查询结果写入Redis缓存+设置过期时间"]
J --> K{DB是否无数据?}
K -- 是 --> L["执行空值回调cacheGetIfAbsent"]
K -- 否 --> M["缓存回填完成"]
L & M --> H
四、完整核心源码
/**
* 安全获取缓存(防穿透、防击穿、高性能通用方案)
*
* @param key 缓存Key
* @param clazz 返回数据泛型类型
* @param cacheLoader 缓存未命中时,DB数据加载器
* @param timeout 缓存过期时间
* @param timeUnit 过期时间单位
* @param bloomFilter 布隆过滤器:防缓存穿透
* @param cacheGetFilter 自定义Key过滤规则
* @param cacheGetIfAbsent 数据库无数据时自定义回调
* @return 缓存/数据库查询结果
*/
@Override
public <T> T safeGet(
String key,
Class<T> clazz,
CacheLoader<T> cacheLoader,
long timeout,
TimeUnit timeUnit,
RBloomFilter<String> bloomFilter,
CacheGetFilter<String> cacheGetFilter,
CacheGetIfAbsent<String> cacheGetIfAbsent) {
// 1. 无锁查询Redis缓存,优先走缓存,保证高并发性能
T result = get(key, clazz);
// 2. 快速熔断:满足任一条件直接返回,拦截无效请求
if (!CacheUtil.isNullOrBlank(result)
|| Optional.ofNullable(cacheGetFilter).map(each -> each.filter(key)).orElse(false)
|| Optional.ofNullable(bloomFilter).map(each -> !each.contains(key)).orElse(false)) {
return result;
}
// 3. 缓存未命中:加分布式锁,防止热点Key缓存击穿
RLock lock = redissonClient.getLock(SAFE_GET_DISTRIBUTED_LOCK_KEY_PREFIX + key);
lock.lock();
try {
// 4. DCL双重检查:防止多线程重复查询数据库
if (CacheUtil.isNullOrBlank(result = get(key, clazz))) {
// 5. 执行DB查询 + 自动回填Redis缓存
if (CacheUtil.isNullOrBlank(result = loadAndSet(key, cacheLoader, timeout, timeUnit, true, bloomFilter))) {
// 6. 数据库无数据,执行自定义兜底回调
Optional.ofNullable(cacheGetIfAbsent).ifPresent(each -> each.execute(key));
}
}
} finally {
// 7. 强制释放分布式锁,避免死锁
lock.unlock();
}
return result;
}
五、分层逐段源码解析
1. 方法入参设计(高扩展性)
key / clazz:基础缓存标识与泛型类型绑定;cacheLoader:函数式接口,由业务层传入数据库查询逻辑,实现方法通用;timeout / timeUnit:动态设置缓存过期时间,缓解缓存雪崩;bloomFilter:全局布隆过滤器,拦截不存在的非法 Key;- 两个自定义函数式回调:适配不同业务的过滤、空值兜底需求。
2. 一级缓存查询(高性能基石)
T result = get(key, clazz);
3. 三层熔断拦截(杜绝缓存穿透)
if (!CacheUtil.isNullOrBlank(result)
|| Optional.ofNullable(cacheGetFilter).map(each -> each.filter(key)).orElse(false)
|| Optional.ofNullable(bloomFilter).map(each -> !each.contains(key)).orElse(false)) {
return result;
}
- 缓存有值:直接返回;
- 自定义过滤器:过滤无效业务 Key;
- 布隆过滤器:若 Key 从未存入,直接拦截,拒绝查询 DB。
4. 分布式锁(解决缓存击穿)
RLock lock = redissonClient.getLock(SAFE_GET_DISTRIBUTED_LOCK_KEY_PREFIX + key);
lock.lock();
热点 Key 过期瞬间,海量并发请求同时失效,通过Redisson 分布式锁保证:同一个 Key,同一时间仅一个线程访问数据库,避免 DB 连接打满。
5. DCL 双重检查(避免重复查库)
if (CacheUtil.isNullOrBlank(result = get(key, clazz)))
抢到锁后二次查询缓存:上一个线程可能已经完成 DB 查询 + 缓存回填,当前线程无需重复查库,节省数据库资源。
6. 数据加载与缓存回填
loadAndSet(key, cacheLoader, timeout, timeUnit, true, bloomFilter)
- 执行业务层传入的
cacheLoader逻辑查询数据库; - 查询完成后自动写入 Redis,并设置过期时间;
- 同步维护布隆过滤器数据,保证后续穿透拦截生效。
7. 空值兜底 & 锁释放
- 数据库无数据时,执行自定义回调(日志记录、默认值填充等);
finally强制释放锁,无论业务异常与否,杜绝死锁问题。
六、解决的三大缓存问题总结
| 问题 | 解决方案 | 核心实现 |
|---|---|---|
| 缓存穿透 | 布隆过滤器 + 自定义 Key 过滤 | 不存在的 Key 直接拦截,不查库 |
| 缓存击穿 | Redisson 分布式锁 + 双重检查 | 热点 Key 失效,单线程查库回填 |
| 缓存雪崩 | 动态过期时间 + 缓存分层 | 避免批量 Key 同时过期,分散 DB 压力 |
七、项目落地使用示例
// 查询车次详情 安全缓存调用
TrainDTO trainDTO = safeGet(
"train:info:" + trainId,
TrainDTO.class,
// 缓存未命中,查询数据库
() -> trainMapper.selectTrainInfo(trainId),
30,
TimeUnit.MINUTES,
bloomFilter,
null,
key -> log.warn("当前Key无数据库数据:{}", key)
);
八、适用场景
- 高并发查询业务:车次、站点、商品、用户信息查询;
- 热点数据缓存:秒杀商品、节假日热门线路;
- 数据一致性要求高、禁止频繁打库的核心业务;
- 所有需要统一规范缓存使用的微服务项目。
九、总结
safeGet 是一套开箱即用、高可用、高并发的缓存标准化方案,摒弃了传统简易缓存写法的各种漏洞,通过「缓存查询→请求拦截→分布式锁→双重检查→自动回填」五层架构,完美解决缓存三大经典问题。代码基于泛型 + 函数式接口设计,通用性极强,可直接复用在电商、出行、金融等各类高并发项目中,也是面试中缓存架构设计的高频标准答案。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)