「性能提升」擴(kuò)展 Spring Cache 支持多級緩存

為什么多級緩存

緩存的引入是現(xiàn)在大部分系統(tǒng)所必須考慮的

  • redis 作為常用中間件览露,雖然我們一般業(yè)務(wù)系統(tǒng)(畢竟業(yè)務(wù)量有限)不會遇到如下圖 在隨著 data-size 的增大和數(shù)據(jù)結(jié)構(gòu)的復(fù)雜的造成性能下降私爷,但網(wǎng)絡(luò) IO 消耗會成為整個調(diào)用鏈路中不可忽視的部分淋样。尤其在 微服務(wù)架構(gòu)中灸撰,一次調(diào)用往往會涉及多次調(diào)用 例如pig oauth2.0 的 client 認(rèn)證
  • Caffeine 來自未來的本地內(nèi)存緩存,性能比如常見的內(nèi)存緩存實(shí)現(xiàn)性能高出不少詳細(xì)對比哨啃。

綜合所述:我們需要構(gòu)建 L1 Caffeine JVM 級別緩存 号阿, L2 Redis 緩存迁沫。

設(shè)計難點(diǎn)

目前大部分應(yīng)用緩存都是基于 Spring Cache 實(shí)現(xiàn),基于注解(annotation)的緩存(cache)技術(shù),存在的問題如下:

  • Spring Cache 僅支持 單一的緩存來源芦瘾,即:只能選擇 Redis 實(shí)現(xiàn)或者 Caffeine 實(shí)現(xiàn),并不能同時使用集畅。
  • 數(shù)據(jù)一致性:各層緩存之間的數(shù)據(jù)一致性問題近弟,如應(yīng)用層緩存和分布式緩存之前的數(shù)據(jù)一致性問題。
  • 緩存過期:Spring Cache 不支持主動的過期策略

業(yè)務(wù)流程

如何使用

    1. 引入依賴
<dependency>
    <groupId>com.pig4cloud.plugin</groupId>
    <artifactId>multilevel-cache-spring-boot-starter</artifactId>
    <version>0.0.1</version>
</dependency>
    1. 開啟緩存支持
@EnableCaching
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
    1. 目標(biāo)接口聲明 Spring Cache 注解
@Cacheable(value = "get",key = "#key")
@GetMapping("/get")
public String get(String key){
    return "success";
}

性能比較

為保證性能 redis 在 127.0.0.1 環(huán)路安裝

  • OS: macOS Mojave
  • CPU: 2.3 GHz Intel Core i5
  • RAM: 8 GB 2133 MHz LPDDR3
  • JVM: corretto_11.jdk
Benchmark Mode Cnt Score Units
多級實(shí)現(xiàn) thrpt 2 2716.074 ops/s
默認(rèn) redis thrpt 2 1373.476 ops/s

代碼原理

    1. 自定義 CacheManager 多級緩存實(shí)現(xiàn)
public class RedisCaffeineCacheManager implements CacheManager {

    @Override
    public Cache getCache(String name) {
        Cache cache = cacheMap.get(name);
        if (cache != null) {
            return cache;
        }
        cache = new RedisCaffeineCache(name, stringKeyRedisTemplate, caffeineCache(), cacheConfigProperties);
        Cache oldCache = cacheMap.putIfAbsent(name, cache);
        log.debug("create cache instance, the cache name is : {}", name);
        return oldCache == null ? cache : oldCache;
    }
}
    1. 多級讀取挺智、過期策略實(shí)現(xiàn)
public class RedisCaffeineCache extends AbstractValueAdaptingCache {
    protected Object lookup(Object key) {
        Object cacheKey = getKey(key);

    // 1. 先調(diào)用 caffeine 查詢是否存在指定的值
        Object value = caffeineCache.getIfPresent(key);
        if (value != null) {
            log.debug("get cache from caffeine, the key is : {}", cacheKey);
            return value;
        }

    // 2. 調(diào)用 redis 查詢在指定的值
        value = stringKeyRedisTemplate.opsForValue().get(cacheKey);

        if (value != null) {
            log.debug("get cache from redis and put in caffeine, the key is : {}", cacheKey);
            caffeineCache.put(key, value);
        }
        return value;
    }
}
    1. 過期策略祷愉,所有更新操作都基于 redis pub/sub 消息機(jī)制更新
public class RedisCaffeineCache extends AbstractValueAdaptingCache {
    @Override
    public void put(Object key, Object value) {
        push(new CacheMessage(this.name, key));
    }

    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
                push(new CacheMessage(this.name, key));
    }

    @Override
    public void evict(Object key) {
        push(new CacheMessage(this.name, key));
    }

    @Override
    public void clear() {
        push(new CacheMessage(this.name, null));
    }

    private void push(CacheMessage message) {
        stringKeyRedisTemplate.convertAndSend(topic, message);
    }
}
    1. MessageListener 刪除指定 Caffeine 的指定值
public class CacheMessageListener implements MessageListener {

    private final RedisTemplate<Object, Object> redisTemplate;

    private final RedisCaffeineCacheManager redisCaffeineCacheManager;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        CacheMessage cacheMessage = (CacheMessage) redisTemplate.getValueSerializer().deserialize(message.getBody());
                cacheMessage.getCacheName(), cacheMessage.getKey());
        redisCaffeineCacheManager.clearLocal(cacheMessage.getCacheName(), cacheMessage.getKey());
    }
}

源碼地址

https://github.com/pig-mesh/multilevel-cache-spring-boot-starter

https://gitee.com/log4j/pig

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市赦颇,隨后出現(xiàn)的幾起案子二鳄,更是在濱河造成了極大的恐慌,老刑警劉巖媒怯,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件订讼,死亡現(xiàn)場離奇詭異,居然都是意外死亡扇苞,警方通過查閱死者的電腦和手機(jī)躯嫉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杨拐,“玉大人,你說我怎么就攤上這事擂啥『逄眨” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵哺壶,是天一觀的道長屋吨。 經(jīng)常有香客問我,道長山宾,這世上最難降的妖魔是什么至扰? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮资锰,結(jié)果婚禮上敢课,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好直秆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布濒募。 她就那樣靜靜地躺著,像睡著了一般圾结。 火紅的嫁衣襯著肌膚如雪瑰剃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天筝野,我揣著相機(jī)與錄音晌姚,去河邊找鬼。 笑死歇竟,一個胖子當(dāng)著我的面吹牛挥唠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播途蒋,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼猛遍,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了号坡?” 一聲冷哼從身側(cè)響起懊烤,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宽堆,沒想到半個月后腌紧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畜隶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年壁肋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片籽慢。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡浸遗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出箱亿,到底是詐尸還是另有隱情跛锌,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布届惋,位于F島的核電站髓帽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏脑豹。R本人自食惡果不足惜郑藏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瘩欺。 院中可真熱鬧必盖,春花似錦拌牲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至阁吝,卻和暖如春砚婆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背突勇。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工装盯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人甲馋。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓埂奈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親定躏。 傳聞我的和親對象是個殘疾皇子账磺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355