什么是緩存
緩存通俗點說就是臨時存儲數(shù)據(jù)的可高速訪問的地方。
當從原始位置獲取數(shù)據(jù)的代價太大或者時間太長的時候,就可以把獲取到的數(shù)據(jù)存放在緩存中,這樣下次訪問的時候就提高了訪問速度降低了訪問成本。
緩存的基本知識點
- 緩存的對象
緩存的對象應該是花費了時間計算或者獲取到的數(shù)據(jù),并且這些數(shù)據(jù)在不久會被訪問到。
在 Java 應用中,常見的緩存例子有如下幾種
- Web Service 客戶端調用結果的緩存
- 獲取到的 DB 數(shù)據(jù)
- Servlet response 的緩存
- 復雜計算的結果
代碼實例如 String 的 hashCode 計算結果
/** Cache the hash code for the string */
private int hash; // Default to 0
public int hashCode() {
//get from cache first
int h = hash;
//cannot found
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
//set to cache
hash = h;
}
return h;
}
- 緩存命中率
緩存命中指的是當前請求發(fā)生在緩存中,就是說數(shù)據(jù)可以從緩存中獲得。
緩存命中率=緩存命中次數(shù)/總請求數(shù)[從緩存中讀取+慢速設備讀取]
緩存命中率是衡量緩存性能的一個重要指標。
- 緩存清除策略
當緩存失效或者緩存空間不足的時候,需要提供緩存算法來清除一些不再使用的緩存數(shù)據(jù),這個時候就涉及到了緩存清除策略,一般稱做緩存算法。
常見的緩存算法有:
- Least Recently Used (LRU)
- Most Recently Used (MRU)
- Least-Frequently Used(LFU)
- Low Inter-reference Recency Set (LIRS)
等,具體介紹請參考維基百科或者查閱對應算法資料。
- 緩存存活期 TTL(Time To Live)
存活期即從緩存中創(chuàng)建時間點開始直到它到期的一個時間段(不管在這個時間段內有沒有訪問都將過期) - 緩存空閑期 TTI(Time To Idle)
空閑期即一個數(shù)據(jù)多久沒被訪問將從緩存中移除的時間。
一般緩存使用方式
Spring cache abstraction
Spring cache 抽象層是 Spring 提供的一種能夠極小沖擊現(xiàn)有代碼的緩存機制。它提供的是不依賴具體緩存框架的,使用 AOP 方式實現(xiàn)的緩存機制。
Spring 的緩存技術還具備相當?shù)撵`活性,不僅能夠使用 SpEL(Spring Expression Language)來定義緩存的 key 和各種 condition,還提供開箱即用的緩存臨時存儲方案如 JDK 實現(xiàn)的緩存和 Guava cache,也支持和主流的專業(yè)緩存例如 EHCache 和 Redis 集成。
Spring 緩存技術基于 AOP 實現(xiàn), 因此有兩種實現(xiàn)模式 proxy 和 aspectj 可以選擇以適應不同情況。
Spring cache 的使用方式
Spring cache 提供了一些注解來聲明需要使用 Cache 的方法。
-
@Cacheable
聲明使用 Cache,方式是 put if not found -
@CacheEvict
觸發(fā)緩存清除操作 -
@CachePut
在方法執(zhí)行的時候更新特定緩存,不影響方法執(zhí)行結果 -
@Caching
如果一個方法有多個緩存操作可以使用 -
@CacheConfig
配置當前類中使用的緩存配置
每一個注解的使用說明請看 Spring 的文檔。
使用 Spring Data Redis 實現(xiàn) Cache
Spring data redis 是 Spring 提供的對 Redis 操作進行封裝的框架,同時,他還提供了 Spring cache 的 Redis 實現(xiàn),因此我們可以使用他來完成 Redis 緩存的集成。
Redis
Redis是一個開源、支持網(wǎng)絡、基于內存、鍵值對存儲數(shù)據(jù)庫,可以用作數(shù)據(jù)庫、緩存和消息中間件。將 Redis 作為緩存的時候應該注意:
- 設置最大可用的內存,使用
maxmemory
設置 - 設置 Key 的清除策略(也就是緩存策略),使用
maxmemory-policy
設置
如果不設置最大可用內存的話:32位操作系統(tǒng),最大默認值為 3G,而64位則不限定。這樣就會導致內存占用過多,并且可能會對緩存特定 Key 的檢索速度有影響。
Redis 真正支持的緩存策略只有 LRU,但是我們可以在 redis.conf
中配置以下不同的參數(shù),來實現(xiàn)不同效果的 Key 清除策略:
- noeviction:在客戶端操作會導致更多內存占用的時候拋出錯誤。
- allkeys-lru: 優(yōu)先清除最近最少使用的 key 來為新加數(shù)據(jù)讓出空間。
- volatile-lru: 優(yōu)先在設置了過期時間的 key 中清除最近最少使用的來為新加數(shù)據(jù)讓出空間。
- allkeys-random: 隨機清除 key 來為新加數(shù)據(jù)讓出空間。
- volatile-random: 隨機在設置了過期時間的 key 中清除一些
- volatile-ttl: 在設置了過期時間的 Key 中根據(jù)存活期長短來清除,優(yōu)先清除存活期短的。
如果沒有 Key 滿足條件,volatile-lru、volatile-random 和 volatile-ttl 會和 noeviction 一樣的表現(xiàn)。
配置 Redis 作為 Spring cache 實現(xiàn)
簡單基于注解的配置如下:
@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
@Autowired
private RedisPropertiesVO properties;
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory factory = new JedisConnectionFactory();
factory.afterPropertiesSet();
factory.setHostName(properties.getHost());
factory.setPort(properties.getPort());
factory.setUsePool(true);
factory.setDatabase(properties.getDatabase());
return factory;
}
@Bean
public RedisTemplate<Object, Object> redisTemplate() {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(jedisConnectionFactory());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public CacheManager cacheManager() {
return new RedisCacheManager(redisTemplate());
}
}