天天看點

Spring Cache的基本使用與分析

Spring Cache的基本使用與分析

1|0概述

使用 Spring Cache 可以極大的簡化我們對資料的緩存,并且它封裝了多種緩存,本文基于 redis 來說明。

2|0基本使用

1、所需依賴

org.springframework.bootspring-boot-starter-data-redisorg.springframework.bootspring-boot-starter-cache

2、配置檔案

spring: # redis連接配接資訊 redis: host: 192.168.56.10 port: 6379 cache: # 指定使用的緩存類型 type: redis # 過期時間 redis: time-to-live: 3600000 # 是否開啟字首,預設為true use-key-prefix: true # 鍵的字首,如果不配置,預設就是緩存名cacheNames key-prefix: CACHE_ # 是否緩存空置,防止緩存穿透,預設為true cache-null-values: true

3、Spring Cache 提供的注解如下,使用方法參見:官方文檔,通過這些注解,我們可以友善的操作緩存資料。

@Cacheable:觸發緩存寫入的操作

@CacheEvict:觸發緩存删除的操作

@CachePut:更新緩存,而不會影響方法的執行

@Caching:重新組合要應用于一個方法的多個緩存操作,即對一個方法添加多個緩存操作

@CacheConfig:在類級别共享一些與緩存有關的常見設定

例如,如果需要對傳回結果進行緩存,直接在方法上标注 @Cacheable 注解

@Cacheable(cacheNames = "userList") //指定緩存的名字,便于區分不同緩存 public List getUserList() { ... }

4、redis 預設使用 jdk 序列化,需要我們配置序列化機制,自定義一個配置類,否則存入的資料顯示亂碼

@EnableCaching //開啟緩存 @Configuration public class MyCacheConfig { @Bean public RedisCacheConfiguration redisCacheConfiguration(){ RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); //指定鍵和值的序列化機制 config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())); config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); return config; } }

5、使用以上配置後,雖然亂碼的問題解決了,但配置檔案又不生效了,比如過期時間等,這是因為在初始化時會判斷使用者是否自定義了配置檔案,如果自定義了,原來的就不會生效,源碼如下:

private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(ClassLoader classLoader) { //如果配置了,就傳回自定義的配置 if (this.redisCacheConfiguration != null) { return this.redisCacheConfiguration; } //沒配置使用預設的配置 Redis redisProperties = this.cacheProperties.getRedis(); org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration .defaultCacheConfig(); config = config.serializeValuesWith( SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader))); if (redisProperties.getTimeToLive() != null) { config = config.entryTtl(redisProperties.getTimeToLive()); } if (redisProperties.getKeyPrefix() != null) { config = config.prefixKeysWith(redisProperties.getKeyPrefix()); } if (!redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); } if (!redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); } return config; }

6、是以,我們也需要手動擷取 ttl、prefix 等屬性,直接仿照源碼就行,将配置類修改為如下:

@EnableCaching //開啟緩存 @Configuration @EnableConfigurationProperties(CacheProperties.class) //緩存的所有配置屬性都在這個類裡 public class MyCacheConfig { @Bean public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) { //擷取預設配置 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); //指定鍵和值的序列化機制 config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())); config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); //擷取配置檔案的配置 CacheProperties.Redis redisProperties = cacheProperties.getRedis(); if (redisProperties.getTimeToLive() != null) { config = config.entryTtl(redisProperties.getTimeToLive()); } if (redisProperties.getKeyPrefix() != null) { config = config.prefixKeysWith(redisProperties.getKeyPrefix()); } if (!redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); } if (!redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); } return config; } }

3|0原理分析

在 Spring 中 CacheManager 負責建立管理 Cache,Cache 負責緩存的讀寫,是以使用 redis 作為緩存對應的就有 RedisCacheManager 和 RedisCache。

打開 RedisCache 源碼,我們需要注意這兩個方法:

1、讀取資料,未加鎖

@Override protected Object lookup(Object key) { byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key)); if (value == null) { return null; } return deserializeCacheValue(value); }

2、讀取資料,加鎖,這是 RedisCache 中唯一一個同步方法

@Override public synchronized T get(Object key, Callable valueLoader) { ValueWrapper result = get(key); if (result != null) { return (T) result.get(); } T value = valueFromLoader(key, valueLoader); put(key, value); return value; }

通過打斷點的方式可以知道 RedisCache 預設調用的是 lookup(),是以不能應對緩存穿透,如果有相關需求,可以這樣配置:@Cacheable(sync = true),開啟同步模式,此配置隻在 @Cacheable 中才有。

4|0總結

Spring Cache 對于讀模式下緩存失效的解決方案:

緩存穿透:cache-null-values: true,允許寫入空值

緩存擊穿:@Cacheable(sync = true),加鎖

緩存雪崩:time-to-live:xxx,設定不同的過期時間

而對于寫模式,Spring Cache 并沒有相應處理,我們需要使用其它方式處理。

總的來說:

1、對于正常資料(讀多寫少,及時性、一緻性要求不高的資料)完全可以使用 Spring Cache

2、對于特殊資料(比如要求高一緻性)則需要特殊處理

EOF

本文作者:JLSong

本文連結:

https://www.cnblogs.com/songjilong/p/12901397.html