spring-boot-cache 緩存提升系統(tǒng)響應速度

1. 緩存簡介

緩存:緩存就是數(shù)據(jù)交換的緩沖區(qū)(稱作Cache)纲酗,當某一硬件要讀取數(shù)據(jù)時笋婿,會首先從緩存中查找需要的數(shù)據(jù),如果找到了則直接執(zhí)行勾给,找不到的話則從內存中找滩报。為什么使用緩存?究其原因就是緩存的讀寫速度遠快與磁盤播急,從減輕I/O開銷和加快運行速度方便都有很好的效果脓钾。那么我們緩存什么?哪些經(jīng)常讀取而又不經(jīng)常修改的數(shù)據(jù)桩警,那些數(shù)據(jù)量較大又很少修改的數(shù)據(jù)惭笑。緩存策略三要素:緩存命中率、緩存更新策略生真、最大緩存容量。

cache 可以說是后端提高響應速度捺宗、承載能力的標準套路了
spring boot中提供spring boot starter cache 組件 配合spring boot starter redis 或者其他緩存組件 可以很簡單的使用緩存柱蟀。

2. spring cache 支持的緩存類型

  • Generic
  • JCache (JSR-107)
  • EhCache 2.x
  • Hazelcast
  • Infinispan
  • Redis
  • Guava
  • Simple
    如果不滿足上述的緩存方案 可以自實現(xiàn) cacheManager。

注解介紹

  • @Cacheable

獲取緩存 如果有緩存 直接返回蚜厉。
@Cacheable可以標記在一個方法上长已,也可以標記在一個類上。當標記在一個方法上時表示該方法是支持緩存的昼牛,當標記在一個類上時則表示該類所有的方法都是支持緩存的术瓮。對于一個支持緩存的方法,Spring會在其被調用后將其返回值緩存起來贰健,以保證下次利用同樣的參數(shù)來執(zhí)行該方法時可以直接從緩存中獲取結果胞四,而不需要再次執(zhí)行該方法。Spring在緩存方法的返回值時是以鍵值對進行緩存的伶椿,值就是方法的返回結果辜伟,至于鍵的話,Spring又支持兩種策略脊另,默認策略和自定義策略导狡。

image

value:緩存的名稱,在 spring 配置文件中定義偎痛,必須指定至少一個旱捧。如@Cacheable(value=”mycache”) 或者@Cacheable(value={”cache1”,”cache2”}
key:緩存的 key,可以為空踩麦,如果指定要按照 SpEL 表達式編寫枚赡,如果不指定,則缺省按照方法的所有參數(shù)進行組合靖榕。如@Cacheable(value=”testcache”,key=”#userName”)
condition:緩存的條件标锄,可以為空,使用 SpEL 編寫茁计,返回 true 或者 false料皇,只有為 true 才進行緩存谓松。如@Cacheable(value=”testcache”,condition=”#userName.length()>2”)

  • @CachePut
    執(zhí)行并且更新緩存相關 不管如何 肯定會執(zhí)行方法 然后返回 這樣可以更新緩存的內容,@CachePut也可以標注在類上和方法上践剂。使用@CachePut時我們可以指定的屬性跟@Cacheable是一樣的鬼譬。

在支持Spring Cache的環(huán)境下,對于使用@Cacheable標注的方法逊脯,Spring在每次執(zhí)行前都會檢查Cache中是否存在相同key的緩存元素优质,如果存在就不再執(zhí)行該方法,而是直接從緩存中獲取結果進行返回军洼,否則才會執(zhí)行并將返回結果存入指定的緩存中巩螃。@CachePut也可以聲明一個方法支持緩存功能。與@Cacheable不同的是使用@CachePut標注的方法在執(zhí)行前不會去檢查緩存中是否存在之前執(zhí)行過的結果匕争,而是每次都會執(zhí)行該方法避乏,并將執(zhí)行結果以鍵值對的形式存入指定的緩存中。

  • @CacheEvict
    刪除緩存相關甘桑,

@CacheEvict是用來標注在需要清除緩存元素的方法或類上的拍皮。當標記在一個類上時表示其中所有的方法的執(zhí)行都會觸發(fā)緩存的清除操作。@CacheEvict可以指定的屬性有value跑杭、key铆帽、condition、allEntries和beforeInvocation德谅。其中value爹橱、key和condition的語義與@Cacheable對應的屬性類似。即value表示清除操作是發(fā)生在哪些Cache上的(對應Cache的名稱)女阀;key表示需要清除的是哪個key宅荤,如未指定則會使用默認策略生成的key;condition表示清除操作發(fā)生的條件浸策。allEntries和beforeInvocation冯键。
allEntries:是否清空所有緩存內容,缺省為 false庸汗,如果指定為 true惫确,則方法調用后將立即清空所有緩存。如:@CachEvict(value=”testcache”,allEntries=true) 蚯舱。
beforeInvocation:是否在方法執(zhí)行前就清空改化,缺省為 false,如果指定為 true枉昏,則在方法還沒有執(zhí)行的時候就清空緩存陈肛,缺省情況下,如果方法執(zhí)行拋出異常兄裂,則不會清空緩存句旱。如:@CachEvict(value=”testcache”阳藻,beforeInvocation=true)
allEntries指定為true時,則會清楚所有緩存谈撒。

  • @Caching

@Caching注解可以讓我們在一個方法或者類上同時指定多個Spring Cache相關的注解腥泥。其擁有三個屬性:cacheable、put和evict啃匿,分別用于指定@Cacheable蛔外、@CachePut和@CacheEvict。使用如下

@Caching(cacheable = {@Cacheable(value = "user", key = "#id", condition = "#id != '123'"),
            @Cacheable(value = "user", key = "#id", condition = "#id != '321'")}
    )
    public User findById(String id) {
        System.out.println("執(zhí)行數(shù)據(jù)庫查詢方法");
        return userDao.findById(id);
    }

實踐

  • 引入依賴
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

  • 配置
spring:
  redis:
    host: <ip>
    port: <port>
    password: <password>
  cache:
    # spring cache 緩存類型為redis  也可以是其他的實現(xiàn) 
    type: redis

  • 使用cache

模擬帶緩存的service

package com.ming;

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
//公共配置  可以在類上注釋 注釋本類的 緩存相關公共配置
//@CacheConfig(cacheNames = TestCacheService.CACHE_KEY)
public class TestCacheService {

    public static final String CACHE_KEY = "test-cache";

    /**
     * 獲取信息  第二次訪問會取緩存
     *
     * @author ming
     * @date 2018-07-11 17:41:47
     */
    @Cacheable(cacheNames = CACHE_KEY ,key = "#id")
    public String testCache(String id) {
        return getString(id);
    }

    /**
     * 更新信息   更新緩存
     *
     * @author ming
     * @date 2018-07-12 09:50:53
     */
    @CachePut(cacheNames = CACHE_KEY ,key = "#id")
    public String testCachePut(String id) {
        return getString(id + "update");
    }

    /**
     * 清除緩存
     *
     * @author ming
     * @date 2018-07-12 09:51:22
     */
    @CacheEvict(cacheNames = CACHE_KEY ,key = "#id")
    public void removeCache(String id) {
        System.out.println("刪除緩存 ");
    }

    /**
     * 獲取string 模擬調用方法
     *
     * @author ming
     * @date 2018-07-11 17:41:58
     */
    private String getString(String id) {
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return id + "load";
    }

}

  • 測試用例
package com.ming;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Start.class)
public class TestCache {

    @Autowired
    private TestCacheService testCacheService;

    @Test
    public void test() {
        String id = "ming";
        System.out.println("第一次訪問沒有緩存--------");
        long oneNow = System.currentTimeMillis();
        System.out.println(testCacheService.testCache(id));
        System.out.println("耗時:" + (System.currentTimeMillis() - oneNow) + "ms");

        System.out.println("第二次訪問有緩存--------");
        long twoNow = System.currentTimeMillis();
        System.out.println(testCacheService.testCache(id));
        System.out.println("耗時:" + (System.currentTimeMillis() - twoNow) + "ms");

        System.out.println("更新緩存信息--------");
        long threeNow = System.currentTimeMillis();
        System.out.println(testCacheService.testCachePut(id));
        System.out.println("耗時:" + (System.currentTimeMillis() - threeNow) + "ms");

        System.out.println("獲取更新后的緩存信息-------");
        long fourNow = System.currentTimeMillis();
        System.out.println(testCacheService.testCache(id));
        System.out.println("耗時:" + (System.currentTimeMillis() - fourNow) + "ms");

        System.out.println("移除緩存------并且調用testCache方法");
        testCacheService.removeCache(id);
        long fiveNow = System.currentTimeMillis();
        System.out.println(testCacheService.testCache(id));
        System.out.println("耗時:" + (System.currentTimeMillis() - fiveNow) + "ms");
    }
}

  • 注意事項

@Cacheable 溯乒、@CachePut夹厌、@CacheEvict 必須要有 cacheNames或 value
注解必須放在public修飾的方法上。
如果只是獲取緩存使用@Cacheable即可 如果要更新數(shù)據(jù)庫并且更新緩存一定要使用@CachePut 否則@Cacheable會出現(xiàn)臟讀裆悄。

總結

spring cache 為緩存提供了一套簡單快捷的方案 可以很快速添加上緩存
具體緩存的實現(xiàn) 也有更多的選擇 也可以自己實現(xiàn)spring cache的緩存管理器 來實現(xiàn)自定義的緩存尊流。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市灯帮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逻住,老刑警劉巖钟哥,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞬捕,死亡現(xiàn)場離奇詭異喉祭,居然都是意外死亡涉枫,警方通過查閱死者的電腦和手機髓绽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門挫鸽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盐捷,“玉大人蹬挤,你說我怎么就攤上這事贯底“榘拢” “怎么了写烤?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拾徙。 經(jīng)常有香客問我洲炊,道長,這世上最難降的妖魔是什么尼啡? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任暂衡,我火速辦了婚禮,結果婚禮上崖瞭,老公的妹妹穿的比我還像新娘狂巢。我一直安慰自己,他們只是感情好书聚,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布唧领。 她就那樣靜靜地躺著藻雌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疹吃。 梳的紋絲不亂的頭發(fā)上蹦疑,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音萨驶,去河邊找鬼歉摧。 笑死,一個胖子當著我的面吹牛腔呜,可吹牛的內容都是我干的叁温。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼核畴,長吁一口氣:“原來是場噩夢啊……” “哼膝但!你這毒婦竟也來了?” 一聲冷哼從身側響起谤草,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤跟束,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后丑孩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冀宴,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年温学,在試婚紗的時候發(fā)現(xiàn)自己被綠了略贮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡仗岖,死狀恐怖逃延,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情轧拄,我是刑警寧澤揽祥,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站檩电,受9級特大地震影響盔然,放射性物質發(fā)生泄漏。R本人自食惡果不足惜是嗜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一愈案、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鹅搪,春花似錦站绪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽魂挂。三九已至,卻和暖如春馁筐,著一層夾襖步出監(jiān)牢的瞬間涂召,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工敏沉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留果正,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓盟迟,卻偏偏與公主長得像秋泳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子攒菠,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內容