緩存在開發中是一個必不可少的優化點,近期在項目重構中,有關緩存問題,花費大量的時間去作優化,好比在加載數據比較所的場景中,使用緩存機制來提升接口的響應速度,間接的提升用戶體驗。對於緩存,不少開發者對它都是既愛又恨,愛它的是:可以大幅度提高響應效率,恨的是:若是緩存處理很差、沒有用好緩存策略,沒有及時更新數據庫的數據就會致使數據產生滯後,從而致使用戶體驗較差。這是一個很嚴重的老大難的問題,例如我在開發某項功能須要和其餘項目進行對接的時候,線上老是出現我推送接口數據成功可是網站數據滯後的現象,詢問對方的技術人員,獲得的回覆是緩存問題,只要刪除緩存就沒事了,我只能無奈了……全部說如何處理好緩存,對於咱們開發人員來講,是一個極其棘手的問題。值得慶幸的是,SpringBoot已經提供給開發者很便捷的開發工做,廢話很少了,接下來開始探索SpringBoot的緩存如何使用吧!java
Spring 從3.1 版本開始就定義了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口來統一不一樣的緩存技術,Cache 接口下 Spring 提供了各類的xxxCache的實現,例如RedisCache、EhCacheCache、ConcurrentMapCache等。每次調用須要緩存功能的方法時,Spring會檢查檢查指定參數的指定的目標方法是否已經被調用過。若是有就直接從緩存中獲取方法調用後的結果,若是沒有就調用方法並緩存結果後返回給用戶,下次調用直接從緩存中獲取。mysql
名稱 | 解釋 |
---|---|
Cache | 緩存接口,定義緩存操做。實現有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
CacheManager | 緩存管理器,管理各類緩存(cache)組件 |
@Cacheable | 主要針對方法配置,可以根據方法的請求參數對其進行緩存。主要實現的功能在進行一個讀操做的時候,先從緩存中查詢,若是查找不到,在讀取數據庫。這是緩存的註解最重要的一個方法,基本上咱們的全部緩存實現都要依賴於它 |
@CacheEvict | 主要是配合@Cacheable一塊兒使用的,它的主要做用就是清除緩存。當方法進行一些更新、刪除操做的時候,就須要刪除緩存 |
@CachePut | 這個註解它老是會把數據緩存,而不會去每次作檢查它是否存在 。與@Cacheable區別在因而否每次都調用方法,經常使用於更新 |
@EnableCaching | 開啓基於註解的緩存,主要做用就是全局配置緩存 |
keyGenerator | 緩存數據時key生成策略 |
serialize | 緩存數據時value序列化策略 |
@CacheConfig | 統一配置本類的緩存註解的屬性 |
名稱 | 解釋 |
---|---|
value | 緩存的名稱,在 spring 配置文件中定義,必須指定至少一個。例如: @Cacheable(value=」mycache」) 或者@Cacheable(value={」cache1」,」cache2」} |
key | 緩存的 key,能夠爲空,若是指定要按照 SpEL 表達式編寫,若是不指定,則缺省按照方法的全部參數進行組合。例如:@Cacheable(value=」testcache」,key=」#id」) |
condition | 緩存的條件,能夠爲空,使用 SpEL 編寫,返回 true 或者 false,只有爲 true 才進行緩存/清除緩存。@Cacheable(value=」testcache」,condition=」#userName.length()>2」) |
unless | 否認緩存。當條件結果爲TRUE時,就不會緩存。@Cacheable(value=」testcache」,unless=」#userName.length()>2」) |
allEntries(@CacheEvict ) | 是否清空全部緩存內容,缺省爲 false,若是指定爲 true,則方法調用後將當即清空全部緩存。 @CachEvict(value=」testcache」,allEntries=true) |
beforeInvocation(@CacheEvict) | 是否在方法執行前就清空,缺省爲 false,若是指定爲 true,則在方法尚未執行的時候就清空緩存,缺省狀況下,若是方法執行拋出異常,則不會清空緩存@CachEvict(value=」testcache」,beforeInvocation=true) |
<!-- Springboot devtools熱部署配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <!-- 引入lombok 依賴 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <scope>provided</scope> </dependency> <!-- 引入cache 緩存依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!--MySQL依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- 引入 MyBatis依賴 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
@SpringBootApplication @EnableCaching //開啓緩存 public class CacheApplication{ public static void main(String[] args) { SpringApplication.run(CacheApplication.class, args); } }
@Cacheable註解會先查詢是否已經有緩存,有會使用緩存,沒有則會執行方法並緩存web
@Cacheable(key = "#id", unless = "#result.state==0") public list<SysUser> getLists(Integer uid){ return cacheDao.getListsByUid(uid); }
到這裏,能夠運行程序檢驗緩存功能是否實現了。若有興趣的話,也能夠深刻源碼,查看其餘的屬性,這裏簡單的說下。spring
String[] cacheNames() default {}; //和value註解差很少,二選一 String keyGenerator() default ""; //key的生成器。key/keyGenerator二選一使用 String cacheManager() default ""; //指定緩存管理器 String cacheResolver() default ""; //或者指定獲取解析器 String condition() default ""; //條件符合則緩存 String unless() default ""; //條件符合則不緩存 boolean sync() default false; //是否使用異步模式
當咱們須要緩存的地方愈來愈多,你可使用@CacheConfig(cacheNames = {"myCache"})註解來統一指定value的值,這時可省略value,若是你在你的方法依舊寫上了value,那麼依然以方法的value值爲準。sql
@Service @CacheConfig(cacheNames = "articleCache") public class ArticleService { …… }
CacheConfig 的屬性數據庫
String keyGenerator() default ""; //key的生成器。key/keyGenerator二選一使用 String cacheManager() default ""; //指定緩存管理器 String cacheResolver() default ""; //或者指定獲取解析器
@CachePut註解的做用 主要針對方法配置,可以根據方法的請求參數對其結果進行緩存,和 @Cacheable 不一樣的是,它每次都會觸發真實方法的調用 。簡單來講就是用戶更新緩存數據。但須要注意的是該註解的value 和 key 必須與要更新的緩存相同,也就是與@Cacheable 相同。示例:緩存
@CachePut(value = "cacheTemp", key = "targetClass + #p0") public SysUser updata(SysUser sysUser) { SysUser sysUser = sysUserDao.findAllById(sysUser.getId()); sysUserDao.updata(sysUser); return sysUser; } @Cacheable(value = "cacheTemp", key = "targetClass +#p0")//清空緩存 public SysUser save(SysUser sysUser) { sysUserDao.save(sysUser); return sysUser; }
CachePut 其餘的屬性mybatis
String[] cacheNames() default {}; //與value二選一 String keyGenerator() default ""; //key的生成器。key/keyGenerator二選一使用 String cacheManager() default ""; //指定緩存管理器 String cacheResolver() default ""; //或者指定獲取解析器 String condition() default ""; //條件符合則緩存 String unless() default ""; //條件符合則不緩存
@CachEvict 的做用 主要針對方法配置,可以根據必定的條件對緩存進行清空 。less
@Cacheable(value = "cacheTemp",key = "#id") public SysUser save(SysUser job) { …… } //清除一條緩存,key爲要清空的數據 @CacheEvict(value="cacheTemp",key="#id") public void delect(int id) { …… } //方法調用後清空全部緩存 @CacheEvict(value="accountCache",allEntries=true) public void delectAll() { …… } //方法調用前清空全部緩存 @CacheEvict(value="accountCache",beforeInvocation=true) public void delectAll() { …… }
其餘屬性異步
String[] cacheNames() default {}; //與value二選一 String keyGenerator() default ""; //key的生成器。key/keyGenerator二選一使用 String cacheManager() default ""; //指定緩存管理器 String cacheResolver() default ""; //或者指定獲取解析器 String condition() default ""; //條件符合則清空
這裏使用postman模擬接口請求
首先咱們來增長一篇文章:請求add接口
後臺返回表示成功:
從後臺數據庫看到已經插入數據,它的id是1
在查詢操做中,getArticle,我使用線程睡眠的方式,模擬了5秒的時間來處理耗時性業務,第一次請求確定會查詢數據庫,理論上第二次請求,將會走緩存,咱們來測試一下:首先執行查詢操做
接口響應成功,再看一下後臺打印:表示執行了一次查詢操做,耗時5212秒
好,重點來了,咱們再次請求接口看看會返回什麼?
與上面的比對,此次沒有打印執行數據庫查詢操做,證實沒有走數據庫,而且耗時只有3ms,成功了!緩存發揮做用,從5212秒減少到3秒!大大提高了響應速度,
當咱們進行修改操做的時候,咱們但願緩存的數據被清空:
後臺控制檯打印:
接下來,連續請求三次請求查詢接口,看看會返回什麼?
第一次請求以及結果
連續三次調用查詢接口,雖然每次返回的都是相同的結果,看上去沒有什麼不一樣,可是我卻有很明顯的感覺,就是第一次查詢最慢,第二次、第三次查詢特別快。第一次查詢耗時是5063毫秒,第二次、第三次耗時確實0,這是由於當咱們第一次執行id=2的數據庫查詢操做,那個時候緩存已被清空,是從數據庫中獲取的最新數據並放進緩存,而後後面的幾回查詢都是直接從緩存中獲取數據,因此查詢速度很快。
在刪除操做中,執行了一次刪除,那麼緩存也會被清空,查詢的時候會再次走數據庫,這裏就不在具體展現了。
如何在開發中使用緩存?怎樣合理的使用都是值得咱們學習的地方,緩存能大大提高程序的響應速度,提高用戶體驗,不過它適用的場景也是讀多寫少的業務場景,若是數據頻繁修改,緩存將會失去意義,每次仍是執行的數據庫操做!這裏只是探討了Spring的註解緩存,相對來講仍是比較簡單的,但願起到拋磚引玉的做用.