《高性能Redis组件》落地实现-第03节:高性能Redis组件防缓存击穿、穿透和雪崩的核心设计与实现
作者:冰河
星球:http://m6z.cn/6aeFbs
博客:https://binghe.site
文章汇总:https://binghe.site/md/all/all.html
源码获取地址:https://t.zsxq.com/0dhvFs5oR
沉淀,成长,突破,帮助他人,成就自我。
- 本章难度:★★☆☆☆
- 本章重点:对高性能Redis组件防缓存击穿、穿透和雪崩问题的功能进行设计和实现,从总体上理解高性能Redis组件防缓存击穿、穿透和雪崩问题的核心设计思想,并从全局视角了解高性能Redis组件的设计和架构思想,并能够将其灵活应用到自身实际项目中。
大家好,我是冰河~~
高性能Redis组件的亮点不仅仅在于其性能非常高,更多的是其在设计和实现上彻底解决了缓存击穿、穿透和雪崩问题。
一、前言
经历过高并发、大流量生产场景的小伙伴都知道,缓存作为承接高并发、大流量场景下,读取数据的一种核心技术手段,被广泛应用于各大核心系统中。但是缓存的设计并不是想象中的那么简单,稍有不慎,就会引起缓存击穿、穿透,甚至雪崩的问题。
如何设计和实现一个通用的高性能缓存组件,对使用方屏蔽处理缓存击穿、穿透和雪崩问题的实现细节,业务只需要调用组件对外暴露的简单接口,即可在保证性能的同时,又能彻底解决缓存击穿、穿透和雪崩问题。所以,在此背景下,高性能Redis组件诞生了。
二、本节诉求
对高性能Redis组件防缓存击穿、穿透和雪崩问题的功能进行设计和实现,从总体上理解高性能Redis组件防缓存击穿、穿透和雪崩问题的核心设计思想,并从全局视角了解高性能Redis组件的设计和架构思想,并能够将其灵活应用到自身实际项目中。
三、核心类设计
注意:本节只给大家展示高性能Redis组件防缓存击穿、穿透和雪崩的的核心类实现关系,其他代码的实现细节,大家可以自行到本节对应的源码分支进行查看,这里不再赘述。
高性能Redis组件分布式锁的核心类设计如图3-1所示。

可以看到,高性能Redis组件中,最核心的实现就是DistributeCacheService接口和RedisDistributeCacheService实现类。
- DistributeCacheService接口:定义了各种缓存操作方法,并封装了获取Key、Value以及结果数据的默认方法。
- RedisDistributeCacheService类:组件最核心的实现,实现了DistributeCacheService接口,并在方法的实现中,彻底解决了缓存击穿、穿透和雪崩问题。
四、编码实现
本节只给大家展示高性能Redis组件解的核心类编码实现,其他代码的实现细节,大家可以自行到本节对应的源码分支进行查看,这里不再赘述。
(1)实现DistributeCacheService接口
定义了各种缓存操作方法,并封装了获取Key、Value以及结果数据的默认方法。
源码详见:io.binghe.redis.plugin.cache.DistributeCacheService。
public interface DistributeCacheService {
/**
* 永久缓存
* @param key 缓存key
* @param value 缓存value
*/
void set(String key, Object value);
/**
* 将数据缓存一段时间
* @param key 缓存key
* @param value 缓存value
* @param timeout 物理缓存的时长
* @param unit 物理时间单位
*/
void set(String key, Object value, Long timeout, TimeUnit unit);
/**
* 设置缓存过期
* @param key 缓存key
* @param timeout 过期时长
* @param unit 时间单位
* @return 设置过期时间是否成功
*/
Boolean expire(String key, final long timeout, final TimeUnit unit);
/**
* 保存缓存时设置逻辑过期时间
* @param key 缓存key
* @param value 缓存value
* @param timeout 缓存逻辑过期时长
* @param unit 缓存逻辑时间单位
*/
void setWithLogicalExpire(String key, Object value, Long timeout, TimeUnit unit);
/**
* 获取缓存中的数据
* @param key 缓存key
* @return 缓存value
*/
String get(String key);
/**
* 获取缓存数据
* @param key 缓存的key
* @param targetClass 目标对象Class
* @param <T> 泛型
* @return 返回的数据
*/
<T> T getObject(String key, Class<T> targetClass);
/**
* 根据key列表批量获取value
* @param keys key列表
* @return value集合
*/
List<String> multiGet(Collection<String> keys);
/**
* 根据正则表达式获取所有的key集合
* @param pattern 正则表达式
* @return key的集合
*/
Set<String> keys(String pattern);
/**
* 删除指定的key
* @param key key
* @return 删除是否成功
*/
Boolean delete(String key);
/**
* 带参数查询对象和简单类型数据,防止缓存穿透
* @param keyPrefix 缓存key的前缀
* @param id 缓存的业务标识,
* @param type 缓存的实际对象类型
* @param dbFallback 查询数据库的Function函数
* @param timeout 缓存的时长
* @param unit 时间单位
* @return 返回业务数据
* @param <R> 结果泛型
* @param <ID> 查询数据库参数泛型,也是参数泛型类型
*/
<R,ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long timeout, TimeUnit unit);
/**
* 不带参数查询对象和简单类型数据,防止缓存穿透
* @param keyPrefix key的前缀
* @param type 缓存的实际对象类型
* @param dbFallback 无参数查询数据库数据
* @param timeout 缓存的时长
* @param unit 时间单位
* @return 返回业务数据
* @param <R> 结果泛型
*/
<R> R queryWithPassThroughWithoutArgs(String keyPrefix, Class<R> type, Supplier<R> dbFallback, Long timeout, TimeUnit unit);
/**
* 带参数查询集合数据,防止缓存穿透
* @param keyPrefix 缓存key的前缀
* @param id 缓存的业务标识,
* @param type 缓存的实际对象类型
* @param dbFallback 查询数据库的Function函数
* @param timeout 缓存的时长
* @param unit 时间单位
* @return 返回业务数据
* @param <R> 结果泛型
* @param <ID> 查询数据库参数泛型,也是参数泛型类型
*/
<R,ID> List<R> queryWithPassThroughList(String keyPrefix, ID id, Class<R> type, Function<ID, List<R>> dbFallback, Long timeout, TimeUnit unit);
/**
* 不带参数查询集合数据,防止缓存穿透
* @param keyPrefix 缓存key的前缀
* @param type 缓存的实际对象类型
* @param dbFallback 无参数查询数据库数据
* @param timeout 缓存的时长
* @param unit 时间单位
* @return 返回业务数据
* @param <R> 结果泛型
*/
<R> List<R> queryWithPassThroughListWithoutArgs(String keyPrefix, Class<R> type, Supplier<List<R>> dbFallback, Long timeout, TimeUnit unit);
/**
* 带参数查询数据,按照逻辑过期时间读取缓存数据,新开线程重建缓存,其他线程直接返回逻辑过期数据,不占用资源
* @param keyPrefix 缓存key的前缀
* @param id 缓存业务标识,也是查询数据库的参数
* @param type 缓存的实际对象类型
* @param dbFallback 查询数据库的Function函数
* @param timeout 缓存逻辑过期时长
* @param unit 缓存逻辑过期时间单位
* @return 业务数据
* @param <R> 结果数据泛型类型
* @param <ID> 查询数据库泛型类型,也是参数泛型类型
*/
<R, ID> R queryWithLogicalExpire(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long timeout, TimeUnit unit);
/**
* 不带参数查询数据,按照逻辑过期时间读取缓存数据,新开线程重建缓存,其他线程直接返回逻辑过期数据,不占用资源
* @param keyPrefix 缓存key的前缀
* @param type 缓存的实际对象类型
* @param dbFallback 无参数查询数据库数据
* @param timeout 缓存的时长
* @param unit 时间单位
* @return 返回业务数据
* @param <R> 结果泛型
*/
<R> R queryWithLogicalExpireWithoutArgs(String keyPrefix, Class<R> type, Supplier<R> dbFallback, Long timeout, TimeUnit unit);
/**
* 带参数查询集合数据,按照逻辑过期时间读取缓存数据,新开线程重建缓存,其他线程直接返回逻辑过期数据,不占用资源
* @param keyPrefix 缓存key的前缀
* @param id 缓存业务标识,也是查询数据库的参数
* @param type 缓存的实际对象类型
* @param dbFallback 查询数据库的Function函数
* @param timeout 缓存逻辑过期时长
* @param unit 缓存逻辑过期时间单位
* @return 业务数据
* @param <R> 结果数据泛型类型
* @param <ID> 查询数据库泛型类型,也是参数泛型类型
*/
<R, ID> List<R> queryWithLogicalExpireList(String keyPrefix, ID id, Class<R> type, Function<ID, List<R>> dbFallback, Long timeout, TimeUnit unit);
/**
* 不带参数查询集合数据,按照逻辑过期时间读取缓存数据,新开线程重建缓存,其他线程直接返回逻辑过期数据,不占用资源
* @param keyPrefix 缓存key的前缀
* @param type 缓存的实际对象类型
* @param dbFallback 无参数查询数据库数据
* @param timeout 缓存的时长
* @param unit 时间单位
* @return 返回业务数据
* @param <R> 结果泛型
*/
<R> List<R> queryWithLogicalExpireListWithoutArgs(String keyPrefix, Class<R> type, Supplier<List<R>> dbFallback, Long timeout, TimeUnit unit);
/**
* 带参数查询数据,按照互斥锁方式获取缓存数据,同一时刻只有一个线程访问数据库,其他线程访问不到数据重试
* @param keyPrefix 缓存key的前缀
* @param id 缓存业务标识,也是查询数据库的参数
* @param type 缓存的实际对象类型
* @param dbFallback 查询数据库的Function函数
* @param timeout 缓存时长
* @param unit 时间单位
* @return 业务数据
* @param <R> 结果数据泛型类型
* @param <ID> 查询数据库泛型类型,也是参数泛型类型
*/
<R, ID> R queryWithMutex(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long timeout, TimeUnit unit);
/**
* 不带参数查询数据,按照互斥锁方式获取缓存数据,同一时刻只有一个线程访问数据库,其他线程访问不到数据重试
* @param keyPrefix 缓存key的前缀
* @param type 缓存的实际对象类型
* @param dbFallback 无参数查询数据库数据
* @param timeout 缓存时长
* @param unit 时间单位
* @return 返回业务数据
* @param <R> 结果泛型
*/
<R> R queryWithMutexWithoutArgs(String keyPrefix, Class<R> type, Supplier<R> dbFallback, Long timeout, TimeUnit unit);
/**
* 带参数查询数据,按照互斥锁方式获取缓存数据,同一时刻只有一个线程访问数据库,其他线程访问不到数据重试
* @param keyPrefix 缓存key的前缀
* @param id 缓存业务标识,也是查询数据库的参数
* @param type 缓存的实际对象类型
* @param dbFallback 查询数据库的Function函数
* @param timeout 缓存时长
* @param unit 时间单位
* @return 业务数据
* @param <R> 结果数据泛型类型
* @param <ID> 查询数据库泛型类型,也是参数泛型类型
*/
<R, ID> List<R> queryWithMutexList(String keyPrefix, ID id, Class<R> type, Function<ID, List<R>> dbFallback, Long timeout, TimeUnit unit);
/**
* 不带参数查询数据,按照互斥锁方式获取缓存数据,同一时刻只有一个线程访问数据库,其他线程访问不到数据重试
* @param keyPrefix 缓存key的前缀
* @param type 缓存的实际对象类型
* @param dbFallback 无参数查询数据库数据
* @param timeout 缓存时长
* @param unit 时间单位
* @return 返回业务数据
* @param <R> 结果泛型
*/
<R> List<R> queryWithMutexListWithoutArgs(String keyPrefix, Class<R> type, Supplier<List<R>> dbFallback, Long timeout, TimeUnit unit);
/**
* 将对象类型的json字符串转换成泛型类型
* @param obj 未知类型对象
* @param type 泛型Class类型
* @return 泛型对象
* @param <R> 泛型
*/
default <R> R getResult(Object obj, Class<R> type){
if (obj == null){
return null;
}
//简单类型
if (TypeConversion.isSimpleType(obj)){
return Convert.convert(type, obj);
}
return JSONUtil.toBean(JSONUtil.toJsonStr(obj), type);
}
/**
* 将对象类型的json字符串转换成泛型类型的List集合
* @param str json字符串
* @param type 泛型Class类型
* @return 泛型List集合
* @param <R> 泛型
*/
default <R> List<R> getResultList(String str, Class<R> type){
if (StrUtil.isEmpty(str)){
return null;
}
return JSONUtil.toList(JSONUtil.parseArray(str), type);
}
/**
* 获取简单的key
* @param key key
* @return 返回key
*/
default String getKey(String key){
return getKey(key, null);
}
/**
* 不确定参数类型的情况下,使用MD5计算参数的拼接到Redis中的唯一Key
* @param keyPrefix 缓存key的前缀
* @param id 泛型参数
* @return 拼接好的缓存key
* @param <ID> 参数泛型类型
*/
default <ID> String getKey(String keyPrefix, ID id){
if (id == null){
return keyPrefix;
}
String key = "";
//简单数据类型与简单字符串
if (TypeConversion.isSimpleType(id)){
key = StrUtil.toString(id);
}else {
key = MD5.create().digestHex(JSONUtil.toJsonStr(id));
}
if (StrUtil.isEmpty(key)){
key = "";
}
return keyPrefix.concat(key);
}
/**
* 获取要保存到缓存中的value字符串,可能是简单类型,也可能是对象类型,也可能是集合数组等
* @param value 要保存的value值
* @return 处理好的字符串
*/
default String getValue(Object value){
return TypeConversion.isSimpleType(value) ? String.valueOf(value) : JSONUtil.toJsonStr(value);
}
}
(2)实现RedisDistributeCacheService类
RedisDistributeCacheService类是组件最核心的实现,实现了DistributeCacheService接口,并在方法的实现中,彻底解决了缓存击穿、穿透和雪崩问题。
源码详见:io.binghe.redis.plugin.cache.redis.RedisDistributeCacheService。
查看完整文章
加入冰河技术知识星球,解锁完整技术文章、小册、视频与完整代码
