缓存(缓存和下载的区别)

  本篇文章为你整理了缓存(缓存和下载的区别)的详细内容,包含有缓存是什么意思? 缓存和下载的区别 缓存视频合并app下载 缓存怎么清除干净 缓存,希望能帮助你了解 缓存。

  解决不同设备间速度不匹配问题。
 

  互联网分层架构:降低数据库压力,提升系统整体性能,缩短访问时间。

  高并发问题

  缓存并发(击穿):缓存过期后将尝试从后端数据库获取数据

  缓存穿透:不存在的 key,请求直接落库查询

  缓存雪崩:缓存大面积失效,请求直接落库查询

  
通过在方法上增加缓存注解,调用方法时根据指定 key 缓存返回数据,再次调用从缓存中获取

  可通过注解指定不同的缓存时长

  避免缓存击穿:缓存失效后使用互斥锁限制查库数量

  避免缓存穿透:对于 null 支持短时间存储

  避免缓存雪崩:可支持每个 key 增加随机时长

  
利用 Spring Cache 处理 Redis 缓存数据
 

  Spring Cache 注解 @Cacheable 携带的 sync() 属性可支持互斥锁限制单个线程处理,可避免缓存击穿
 

  注意开启 Spring Cache 需要在配置类(或启动类)上增加 @EnableCaching

  1 :缓存管理器注入配置时,处理 缓存空间 cacheNames() / value() 与时长对应

  可根据 配置或代码写死 指定不同 缓存空间 缓存时长
 

  此方式以 缓存空间名 为标识区分时长,未配置的缓存空间走全局设定

  使用示例:

  

@GetMapping("/spel2")

 

  @Cacheable(cacheNames = "prefix")

  public ResponseResult String spel2(Long id) {

   id += RandomUtil.randomLong();

   log.info("hello:spel2:{}", id);

   return ResponseResult.success("hello:spel2:" + id);

  

 

  点击查看代码 ExpandRedisConfig.java

  

# yml 配置

 

  expand-cache-config:

   ttl-map: {"yml-ttl":1000,"hello":2000}

  // 引入配置

  @Value("#{${expand-cache-config.ttl-map:null}}")

  private Map String, Long ttlMap;

   * 注入缓存管理器及处理配置中的缓存时长

  @Bean(BEAN_REDIS_CACHE_MANAGER)

  public RedisCacheManager expandRedisCacheManager(RedisConnectionFactory factory) {

   使用 Jackson 作为值序列化处理器

   FastJson 存在部分转换问题如:Set 存储后因为没有对应的类型保存无法转换为 JSONArray(实现 List ) 导致失败

   ObjectMapper om = JsonUtil.createJacksonObjectMapper();

   GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer(om);

   // 配置key、value 序列化(解决乱码的问题)

   RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()

   // key 使用 string 序列化方式

   .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer.UTF_8))

   // value 使用 jackson 序列化方式

   .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer))

   // 配置缓存空间名称前缀

   .prefixCacheNameWith("spring:cache:")

   // 配置全局缓存过期时间

   .entryTtl(Duration.ofMinutes(30L));

   // 专门指定某些缓存空间的配置,如果过期时间,这里的 key 为缓存空间名称

   Map String, RedisCacheConfiguration configMap = new HashMap ();

   // 代码写死示例

   configMap.put("world", config.entryTtl(Duration.ofSeconds(60)));

   Set Map.Entry String, Long entrySet =

   Optional.ofNullable(ttlMap).map(Map::entrySet).orElse(Collections.emptySet());

   for (Map.Entry String, Long entry : entrySet) {

   // 指定特定缓存空间对应的过期时间

   configMap.put(entry.getKey(), config.entryTtl(Duration.ofSeconds(entry.getValue())));

   RedisCacheWriter redisCacheWriter = RedisCacheWriter.lockingRedisCacheWriter(factory);

   // 使用自定义缓存管理器附带自定义参数随机时间,注意此处为全局设定,5-最小随机秒,30-最大随机秒

   return new ExpandRedisCacheManager(redisCacheWriter, config, configMap, 5, 30);

  

 

  2 :在缓存空间名 cacheNames() / value() 中附带时间字符串

  自定义缓存管理器继承 RedisCacheManager,重写创建缓存处理器方法,拿到缓存空间名与缓存配置进行更新缓存时长处理
 

  此方式以 缓存空间名中非指定时间部分 为标识区分时长,缓存空间名不指定时间走全局设定

  使用示例:

  

@GetMapping("/spel2")

 

  @Cacheable(cacheNames = "prefix#5m", cacheManager = ExpandRedisConfig.BEAN_REDIS_CACHE_MANAGER)

  public ResponseResult String spel2(Long id) {

   id += RandomUtil.randomLong();

   log.info("hello:spel2:{}", id);

   return ResponseResult.success("hello:spel2:" + id);

  

 

  点击查看代码 ExpandRedisCacheManager.java

  

@Override

 

  protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {

   String theName = name;

   if (name.contains(NAME_SPLIT_SYMBOL)) {

   // 名称中存在#标记,修改实际名称,替换默认配置的缓存时长为指定缓存时长

   String[] nameArr = name.split(NAME_SPLIT_SYMBOL);

   theName = nameArr[0];

   Duration duration = TimeUtil.parseDuration(nameArr[1]);

   if (duration != null) {

   cacheConfig = cacheConfig.entryTtl(duration);

   // 使用自定义缓存处理器附带自定义参数随机时间,将注入的随机时间传递

   return new ExpandRedisCache(theName, cacheWriter, cacheConfig, this.minRandomSecond,

   this.maxRandomSecond);

  

 

  3 :自定义缓存注解支持 Spring Cache

  自定义注解 使用 @Cacheable 标识,可支持 Spring Cache 处理

  自定义注解增加设置缓存时长的属性: timeout() + unit() ,需要与注入注解的初始化配置方生效

  自定义缓存注解过期时间初始化配置,利用 Spring Component Bean 获取到使用自定义注解的方法,利用反射获取注解属性并设置缓存空间过期时间;Map 处理,同一名称缓存空间将会出现替换情景

  此处理实质仍是以缓存空间名 cacheNames() / value() 中非时间部分为标识区分时长

  使用示例:

  

@GetMapping("/spel3")

 

  @ExpandCacheable(cacheNames = "prefix", spelKey = "hello-spel3-${#id}", timeout = 100,

   unit = TimeUnit.SECONDS)

  public ResponseResult String spel3(Long id) {

   id += RandomUtil.randomLong();

   log.info("hello:spel3:{}", id);

   return ResponseResult.success("hello:spel3:" + id);

  

 

  点击查看代码 ExpandCacheExpireConfig.java

  

/**

 

   * Spring Bean 加载后处理

   * 获取所有 @Component 注解的 Bean 判断类中方法是否存在 @SpringCacheable 注解,存在进行过期时间设置

  @PostConstruct

  public void init() {

   Map String, Object beanMap = beanFactory.getBeansWithAnnotation(Component.class);

   if (MapUtil.isEmpty(beanMap)) {

   return;

   beanMap.values().forEach(item -

   ReflectionUtils.doWithMethods(item.getClass(), method - {

   ReflectionUtils.makeAccessible(method);

   putConfigTtl(method);

   expandRedisCacheManager.initializeCaches();

   * 利用反射设置方法注解上配置的过期时间

   * @param method 注解了自定义缓存的方法

  private void putConfigTtl(Method method) {

   ExpandCacheable annotation = method.getAnnotation(ExpandCacheable.class);

   if (annotation == null) {

   return;

   String[] cacheNames = annotation.cacheNames();

   if (ArrayUtil.isEmpty(cacheNames)) {

   cacheNames = annotation.value();

   // 反射获取缓存管理器初始化配置并设值

   Map String, RedisCacheConfiguration initialCacheConfiguration =

   (Map String, RedisCacheConfiguration )

   ReflectUtil.getFieldValue(expandRedisCacheManager, "initialCacheConfiguration");

   RedisCacheConfiguration defaultCacheConfig =

   (RedisCacheConfiguration)

   ReflectUtil.getFieldValue(expandRedisCacheManager, "defaultCacheConfig");

   Duration ttl = Duration.ofSeconds(annotation.unit().toSeconds(annotation.timeout()));

   for (String cacheName : cacheNames) {

   initialCacheConfiguration.put(cacheName, defaultCacheConfig.entryTtl(ttl));

  

 

  4 :自定义缓存处理器,在设置缓存时处理时长

  继承 Spring 缓存处理器 RedisCache ,重写设置缓存方法
 

  可针对 null 进行短时间存储避免缓存穿透、增加随机时长避免缓存雪崩

  
public void put(Object key, @Nullable Object value) {

   Object cacheValue = preProcessCacheValue(value);

   // 替换父类设置缓存时长处理

   Duration duration = getDynamicDuration(cacheValue);

   cacheWriter.put(name, createAndConvertCacheKey(key),

   serializeCacheValue(cacheValue), duration);

   * 获取动态时长

  private Duration getDynamicDuration(Object cacheValue) {

   // 如果缓存值为 null,固定返回时长为 30s 避免缓存穿透

   if (NullValue.INSTANCE.equals(cacheValue)) {

   return Duration.ofSeconds(30);

   int randomInt = RandomUtil.randomInt(this.minRandomSecond, this.maxRandomSecond);

   return this.cacheConfig.getTtl().plus(Duration.ofSeconds(randomInt));

  

 

 

  优点:使用 Spring 自带功能,通用性强

  缺点:针对缓存空间处理缓存时长,缓存时间一致可能导致缓存雪崩,自定义处理需要理解相应源码实现

  参考:

  Spring cache整合Redis,并给它一个过期时间!

  让 @Cacheable 可配置 Redis 过期时间

  @Cacheable注解配合Redis设置缓存随机失效时间

  聊聊如何基于spring @Cacheable扩展实现缓存自动过期时间以及自动刷新

  SpringBoot实现Redis缓存(SpringCache+Redis的整合)

  


@GetMapping("/default/all")

 

  @MethodCacheable(key = "hello-all", keyType = AspectKeyTypeEnum.DEFAULT, unless = "${#id 0}",

   timeout = 300, unit = TimeUnit.SECONDS, addRandomDuration = false, useLocal = true,

   localTimeout = 60)

  public ResponseResult String exactMatchAll(Long id) {

   id += RandomUtil.randomLong();

   log.info("custom:all:{}", id);

   return ResponseResult.success("custom:all:" + id);

  

 

  2. 注解属性定义

  支持不同类型缓存 key: key() + keyType()

  支持依据条件( SpEL 表达式)设定排除不走缓存: unless()

  支持缓存 key 自定义过期时长( Redis 缓存): timeout() + unit()

  支持缓存 key 自定义过期时长增加随机时长( Redis 缓存): addRandomDuration() ,注意固定了随机范围,可避免缓存雪崩

  支持本地缓存设置:useLocal() + localTimeout() ,注意本地缓存存在全局最大时长限制

  3. AOP 切面处理

  缓存存储数据时加锁(synchronized)执行,避免缓存击穿

  对 null 值进行固定格式字符串缓存,避免缓存穿透

  
@Around("@annotation(cn.eastx.practice.demo.cache.config.custom.MethodCacheable)")

  public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

   MethodCacheableOperation operation = MethodCacheableOperation.convert(joinPoint);

   if (Objects.isNull(operation)) {

   return joinPoint.proceed();

   Object result = getCacheData(operation);

   if (Objects.nonNull(result)) {

   return convertCacheData(result);

   // 加锁处理同步执行

   synchronized (operation.getKey().intern()) {

   result = getCacheData(operation);

   if (Objects.nonNull(result)) {

   return convertCacheData(result);

   result = joinPoint.proceed();

   setDataCache(operation, result);

   return result;

   * 设置数据缓存

   * 特殊值缓存需要转换,特殊值包括 null

   * @param operation 操作对象

   * @param data 数据

  private void setDataCache(MethodCacheableOperation operation, Object data) {

   // null缓存处理,固定存储时长,防止缓存穿透

   if (Objects.isNull(data)) {

   redisUtil.setEx(operation.getKey(), NULL_VALUE, SPECIAL_VALUE_DURATION);

   return;

   // 存在实际数据缓存处理

   redisUtil.setEx(operation.getKey(), data, operation.getDuration());

   if (Boolean.TRUE.equals(operation.getUseLocal())) {

   LocalCacheUtil.put(operation.getKey(), data, operation.getLocalDuration());

  

 

 

  优点:自定义 Spring AOP 实现,可定制化处理程度较高,当前以支持两级缓存(Redis 缓存 + 本地缓存)

  缺点:相对于 Spring 自带 Cache ,部分功能存在缺失不够完善

  以上就是缓存(缓存和下载的区别)的详细内容,想要了解更多 缓存的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: