spring-cache 數(shù)據(jù)庫一致性解決方案

概述

本文先簡單介紹spring-cache的使用即流程系任。再來了解使用cache會帶來與數(shù)據(jù)庫不一致的問題

  • spring-cache使用介紹
  • spring-cache實現(xiàn)原理
  • 使用redis緩存是數(shù)據(jù)庫一致性解決方案

spring-cache使用介紹

spring-cache支持注解(annotation)和xml兩種配置.本次只展示注解(annotation)的使用.它本質(zhì)上不是一個具體的緩存實現(xiàn)方案(例如 EHCache和Redis),而是一個對緩存使用的抽象框架

同時spring-cache還有一個強大的地方就是配置SpEL表達式來定義各種緩存的key和condition,還提供開箱即用的緩存臨時存儲方案慌烧,也支持和主流的專業(yè)緩存例如 EHCache 集成负乡。

1.spring-cache使用

spring-cache常用注解:

  • @Cacheable (存在緩存則直接返回,不存在則調(diào)用業(yè)務(wù)方法,保存到緩存)
  • @CacheEvict (清楚緩存,可清楚cache里全部緩存)
  • @CachePut (不管緩存存不存在,調(diào)用業(yè)務(wù)方法,將返回值set到緩存里面)
    示例代碼:
public class AccountService { 
  @Cacheable(value="accountCache", key="#userName", )// 使用了一個緩存名叫 accountCache,key為userName,value為Account對象
  public Account getAccountByName(String userName) { 
    // 方法內(nèi)部實現(xiàn)不考慮緩存邏輯贯吓,直接實現(xiàn)業(yè)務(wù)
    System.out.println("real query account."+userName); 
    return getFromDB(userName); 
  } 

  @CacheEvict(value="accountCache",key="#account.getName()")// 刪除 accountCache 緩存 里面key為userName的緩存
  public void updateAccount(Account account) {
    updateDB(account); 
  } 
  
  @CacheEvict(value="accountCache",allEntries=true)// 清空 accountCache 緩存
  public void reload() { 
  } 

  private Account getFromDB(String acctName) { 
    System.out.println("real querying db..."+acctName); 
    return new Account(acctName); 
  } 

  private void updateDB(Account account) { 
    System.out.println("real update db..."+account.getName()); 
  } 
}

建議:假如使用的cache方案是redis的話,因為大多數(shù)場景都是多個業(yè)務(wù)線使用同一個redis,一不小心定義的的緩存key可能你會相同.所以最好在初始化RedisCacheManager(spring-data-redis.jar)時設(shè)置usePrefix為true.這樣生成的key都會帶cacheName前綴,防止和其他業(yè)務(wù)的key重復(fù)
生成redis-key的代碼:

/**
   * Get the {@link Byte} representation of the given key element using prefix if available.
   */
  public byte[] getKeyBytes() {

    byte[] rawKey = serializeKeyElement();
    if (!hasPrefix()) {
      return rawKey;//沒有前綴直接返回用戶設(shè)置的key
    }

    byte[] prefixedKey = Arrays.copyOf(prefix, prefix.length + rawKey.length);
    System.arraycopy(rawKey, 0, prefixedKey, prefix.length, rawKey.length); //拼裝prefix和key

    return prefixedKey;
  }

  /**
   * @return true if prefix is not empty.
   */
  public boolean hasPrefix() {
    //usePrefix為true時,該prefix為cacheName
    return (prefix != null && prefix.length > 0);
  }

由于這方面的使用網(wǎng)上一大堆,這里就在累述.

2. spring-cache實現(xiàn)原理

本質(zhì)是使用spring-aop實現(xiàn).<cache:annotation-driven>開啟CacheInterceptor注冊到springContext里面.業(yè)務(wù)運行時調(diào)用代理類執(zhí)行方法spring的所有Interceptor方法,里面包括CacheInterceptor.

interceptor.png

上圖表現(xiàn)spring-aop兩種重播方式,體現(xiàn)了aop的兩種配置方式(@Before,@Around)方式.

cacheInterceptor的實現(xiàn)流程:

緩存流程圖.png

上圖用顏色區(qū)分了每個注解具體的作用:
黃色:@CacheEvict 根據(jù)beforInvocation判斷是前置刪除還是后置刪除.默認(rèn)是false后置上出
藍(lán)色:@Cacheable 判斷condition條件是否滿足再去緩存里面獲取數(shù)據(jù),沒有命中最后會更新到緩存里面
綠色:@CachePut 判斷condition條件是否滿足,然后會更新到緩存里面
深藍(lán):@Cacheable@CachePut 兩個都有更新緩存的操作,所以代碼整理到一塊.

3.高并發(fā)使用的問題

  1. 先更新數(shù)據(jù)庫在更新緩存時失敗


    image.png

如上圖redis更新失敗則會造成數(shù)據(jù)不一致的情況,知道緩存超時自動刪除或則下次更新才可能一致

解決辦法:


image.png

如上圖,把刪除緩存方法放前面,加入刪除失敗則不會操作數(shù)據(jù)庫,這樣就不會造成數(shù)據(jù)不一致的情況.就算出現(xiàn)redis刪除成功,但是超時的問題,最多也是多執(zhí)行一次存入緩存的操作.

  1. 高并發(fā)下不一致問題


    image.png

如上圖正好卡在剛刪除緩存就有一個線程來查詢緩存,就會出現(xiàn)redis里面是舊的數(shù)據(jù),數(shù)據(jù)庫時新的數(shù)據(jù).

解決辦法:


cache與數(shù)據(jù)庫一致性.png

如上圖的解決辦法,主要思想就是把可能出現(xiàn)的(刪除,修改)并發(fā)執(zhí)行通過redis的分布式鎖實現(xiàn)串行.這里有個優(yōu)化點就是讀數(shù)據(jù)沒有獲取鎖成功的話會等待200ms在嘗試讀取緩存,不存在則直接讀取數(shù)據(jù)庫返回.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喇聊,一起剝皮案震驚了整個濱河市泪蔫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痊乾,老刑警劉巖皮壁,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異哪审,居然都是意外死亡蛾魄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門湿滓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來滴须,“玉大人,你說我怎么就攤上這事叽奥∪铀” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵朝氓,是天一觀的道長魔市。 經(jīng)常有香客問我,道長赵哲,這世上最難降的妖魔是什么待德? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮枫夺,結(jié)果婚禮上将宪,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好涧偷,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布簸喂。 她就那樣靜靜地躺著,像睡著了一般燎潮。 火紅的嫁衣襯著肌膚如雪喻鳄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天确封,我揣著相機與錄音除呵,去河邊找鬼。 笑死爪喘,一個胖子當(dāng)著我的面吹牛颜曾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秉剑,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼泛豪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了侦鹏?” 一聲冷哼從身側(cè)響起诡曙,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎略水,沒想到半個月后价卤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡渊涝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年慎璧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跨释。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡胸私,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出煤傍,到底是詐尸還是另有隱情盖文,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布蚯姆,位于F島的核電站,受9級特大地震影響洒敏,放射性物質(zhì)發(fā)生泄漏龄恋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一凶伙、第九天 我趴在偏房一處隱蔽的房頂上張望郭毕。 院中可真熱鬧,春花似錦函荣、人聲如沸显押。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乘碑。三九已至挖息,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間兽肤,已是汗流浹背套腹。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留资铡,地道東北人电禀。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像笤休,于是被迫代替她去往敵國和親尖飞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容