title: 學習筆記:cache 和spring cache 技術 2 author: Eric liu tags: [] categories:
- hexo
spring cache 的基本使用
http://jinnianshilongnian.iteye.com/blog/2001040
Spring支援Cache,是針對方法。主要的實現方式為:當我們在呼叫一個快取方法時會把該方法引數和返回結果作為一個鍵值對存放在快取中,等到下次利用同樣的引數來呼叫該方法時將不再執行該方法,而是直接從快取中獲取結果進行返回。註解既可以放在方法上也可以放在類上,放到類上時,表示該類的所有方法都支援快取。
原理:
和 spring 的事務管理類似,spring cache 的關鍵原理就是 spring AOP,通過 spring AOP,其實現了在方法呼叫前、呼叫後獲取方法的入參和返回值,進而實現了快取的邏輯。
複製程式碼
注意:
當一個支援快取的方法在物件內部被呼叫時是不會觸發快取功能的
@Cache*註解要加在 public 方法上
註解最好加在實現類而不是介面的方法上
因此需要注意的點為:
1、鍵如何選取
2、快取的更新策略
主要會使用到的註解有兩個@Cacheable 與@CacheEvict
複製程式碼
1.基本使用
如1中所說,配置spring 配置檔案,建立cacheManger,在下面配置cache片,在方法上標註@cacheable(value= cache片的名) ,引數為入參key,即可使用。
複製程式碼
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="default"/>
</bean>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="accountCache"/>
</bean>
</set>
</property>
</bean>
</beans>
複製程式碼
(2)基本註解(https://www.cnblogs.com/imyijie/p/6518547.html)
三個主要的註解 Cacheable (最常用的註解,用於標註需要快取方法):
@Cacheable
執行後Spring Cache將快取其返回結果
主要的屬性有三個:value、key和condition
value用來指定Cache的名稱
@Cacheable("cache1")//Cache是發生在cache1上的
public User find(Integer id) {}
@Cacheable({"cache1", "cache2"})//Cache是發生在cache1和cache2上的
public User find(Integer id) {}
複製程式碼
key屬性自定義key
key屬性是用來指定Spring快取方法的返回結果時對應的key的。該屬性支援SpringEL表示式。當我們沒有指定該屬性時,Spring將使用預設策略生成key。
下面分析自定義的策略
使用方法引數時我們可以直接使用“#引數名”或者“#p引數index”。
@Cacheable(value="users", key="#id")
@Cacheable(value="users", key="#p0")
@Cacheable(value="users", key="#user.id")
public User find(User user) {
return null;
}
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
return null;
}
@Cacheable(value = "HOTEL", key = "#p0+'_'+#p1")
public User find(String a,String b) {
}
複製程式碼
預設鍵值生成策略:
1 如果方法沒有引數,則使用0作為key。 2 如果只有一個引數的話則使用該引數作為key。 3 如果引數多餘一個的話則使用所有引數的hashCode作為key。
condition屬性指定發生的條件
將滿足條件的結果快取
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
複製程式碼
可以自己指定
預設KeyGenerator
@CacheEvict
是用來標註在需要清除快取元素的方法或類上的。當標記在一個類上時表示其中所有的方法的執行都會觸發快取的清除操作。
@CacheEvict(value="accountCache",key="#account.getName()")// 清空accountCache 快取
public void updateAccount(Account account) {
updateDB(account);
}
@CacheEvict(value="accountCache",allEntries=true)// 清空accountCache 快取
public void reload() {
reloadAll()
}
@Cacheable(value="accountCache",condition="#userName.length() <=4")// 快取名叫 accountCache
public Account getAccountByName(String userName) {
// 方法內部實現不考慮快取邏輯,直接實現業務
return getFromDB(userName);
}
複製程式碼
key表示需要清除的是哪個key,如未指定則會使用預設策略生成的key;condition表示清除操作發生的條件。
allEntries:刪除名稱空間下的所有快取,預設false,key = "#name" 刪除指定id的快取
通過@schedule的方式每10秒清空快取
beforeInvocation屬性
清除操作預設是在對應方法成功執行之後觸發的,即方法如果因為丟擲異常而未能成功返回時也不會觸發清除操作。使用beforeInvocation可以改變觸發清除操作的時間,當我們指定該屬性值為true時,Spring會在呼叫該方法之前清除快取中的指定元素。
@CachePut
CachePut(用於僅存放快取)
區別:cachePut 是放入快取的,每次都執行,如果有condition執行後判斷condition是否放入快取
@CachePut(value = "models", key = "#name")
public TestModel saveModel(String name, String address) {
return new TestModel(name, address);
}
複製程式碼
condition/unless
複製程式碼
其他:
(1)keyGenerator: 實現 org.springframework.cache.interceptor.KeyGenerator 介面的類bean,用於統一自定義生成key
(2)cacheManager: 用於選擇使用哪個cacheManager
(3)cacheResolver: 實現 org.springframework.cache.interceptor.CacheResolver 介面的類bean,用於自定義如何處理快取
(4)sync: 是否同步 從相同key載入值 的方法,快取消失後多執行緒訪問同一個key的方法,只有一個執行緒實際呼叫,其他執行緒等待快取結果
複製程式碼
區別:
@CachePut也可以宣告一個方法支援快取功能。與@Cacheable不同的是使用@CachePut標註的方法在執行前不會去檢查快取中是否存在之前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的快取中。
@Caching
有時候我們可能組合多個Cache註解使用;比如使用者新增成功後,我們要新增id-->user;username--->user;email--->user的快取;此時就需要@Caching組合多個註解標籤了。
@Caching(
put = {
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
}
)
public User save(User user) {
二:
@Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),
@CacheEvict(value = "cache3", allEntries = true) })
public User find(Integer id) {
returnnull;
}
複製程式碼
使用配置:
(1)cacheManager, 預設使用concurrentHashMap做快取
<!--啟用快取註解功能-->
<cache:annotation-driven/>
<!-- 指定key的生成
<cache:annotation-driven key-generator="XXXXXXXXX"/>
-->
<!--spring自己的基於java.util.concurrent.ConcurrentHashMap實現的快取管理器(該功能是從Spring3.1開始提供)-->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="user"/>
</bean>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="另一個快取的名稱"/>
</bean>
</set>
</property>
</bean>
複製程式碼
(2)也可以用guava cache 做快取
<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.cache.guava.GuavaCacheManager">
<property name="cacheSpecification" value="concurrencyLevel=4,expireAfterAccess=3s,expireAfterWrite=100s" />
<property name="cacheNames">
<list>
<value>default</value>
</list>
</property>
</bean>
複製程式碼
配置說明:
<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
<property name="cacheManagers">
<array>
<!-- 優先看看是不是本地快取-->
<ref bean="guavaCacheManager"/>
<ref bean="sedisCacheManager"/>
</array>
</property>
</bean>
複製程式碼
呼叫cacheManager.getCache(cacheName) 時,會先從第一個cacheManager中查詢有沒有cacheName的cache,如果沒有接著查詢第二個,如果最後找不到
<bean id="guavaCacheManager" class="com.xxxx.guava.GuavaCacheManager">
<property name="caches">
<list>
<bean class="com.xxxx.cache.guava.GuavaCacheFactoryBean" name="w-index">
<property name="spec" value="maximumSize=500,expireAfterWrite=1h,recordStats"/>
<property name="allowNullValues" value="false"/>
</bean>
</list>
</property>
</bean>
複製程式碼
除了GuavaCacheManager之外,其他Cache都支援Spring事務的,即如果事務回滾了,Cache的資料也會移除掉。
如 ,ehCacheManager
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml"/>
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManager"/>
<property name="transactionAware" value="true"/>
</bean>
複製程式碼
這樣注入configLocation,會自動根據路徑從classpath下找,比編碼方式簡單
缺點:
1、他的cache功能是基於aop的,所以當內部呼叫方法的時候就不會呼叫cache方法。
http://www.iteye.com/topic/1123669 spring cache 實現二級快取
核心問題
(1)失效時間
spring cache 註解本身不提供,需要在快取manager 上配置,如guava Cache 可以配置
<bean id="guavaCacheManager" class="com.xxxxx.dapp.guava.GuavaCacheManager">
<property name="caches">
<list>
<bean class="com.xxxxx.vc.cache.guava.GuavaCacheFactoryBean" name="w-index">
<property name="spec" value="maximumSize=500,expireAfterWrite=1h,recordStats"/>
<property name="allowNullValues" value="false"/>
</bean>
</list>
</property>
</bean>
複製程式碼
本地快取和分散式快取:
專案中 配置了本地快取和分散式快取,優先檢視本地快取
(2)快取失效前主動重新整理快取
一般快取失效後,會有一些請求會打到後端的資料庫上,這段時間的訪問效能肯定是比有快取的情況要差很多。所以期望在快取即將過期的某一時間點後臺主動去更新快取以確保前端請求的快取命中率
Srping 4.3提供了一個sync引數。是當快取失效後,為了避免多個請求打到資料庫,系統做了一個併發控制優化,同時只有一個執行緒會去資料庫取資料其它執行緒會被阻塞。
複製程式碼