使用NullObject模式優(yōu)雅解決緩存穿透問(wèn)題

緩存穿透的問(wèn)題

通常使用的緩存都是應(yīng)用先檢查緩存中是否存在。如果存在,則直接返回緩存;如果不存在,則查詢數(shù)據(jù)庫(kù),將結(jié)果緩存并返回。
但是查詢的是一個(gè)不存在的數(shù)據(jù),就會(huì)造成每一次請(qǐng)求都查詢DB,這樣緩存就失去了意義。
為了解決這個(gè)問(wèn)題,通常使用特殊字符串標(biāo)記無(wú)用的緩存,比如"EmptyCache",然后在應(yīng)用層做相應(yīng)處理,解決此問(wèn)題。但是這種方式顯得比較粗暴:

  1. 需要在反序列化過(guò)程中,將特殊字符串排除掉,同時(shí)需要將不存在的數(shù)據(jù)標(biāo)記出來(lái),避免再查詢DB。
  2. 如果需要處理不同類型的數(shù)據(jù)的緩存穿透問(wèn)題,是不是多加幾種特殊字符傳。

因此,我們可以引入NullObject模式來(lái)解決此問(wèn)題。它的作用在于提供一個(gè)對(duì)象給指定的類型,用以代替這個(gè)對(duì)象為空的情況。
更具體的信息,可以參考此鏈接:https://en.wikipedia.org/wiki/Null_object

新的方案:以券(Ticket)為例

步驟一、引入抽象類AbstractNullObject

public abstract class AbstractNullObject{
    private Integer id;
    public void markNullObject(){
        this.id = -1;
    }
    public void isNullObject(){
        return id!=null && id<0;
    }
}

一般在設(shè)計(jì)數(shù)據(jù)庫(kù)表時(shí),都有id字段,而且id字段是自增的整數(shù)。我們可以復(fù)用此id字段,并約定一個(gè)規(guī)則:id小0的對(duì)象,都是NullObject。

步驟二、讓Ticket繼承能夠AbstractNullObject,并添加createNullObject函數(shù)

public class Ticket extends  AbstractNullObject{
    public static Ticket createNullObject(String ticketCode){
        Ticket ticket =new Ticket();
        ticket.setTicketCode(ticketCode):
        ticket.markNullObject();
    }
}

步驟三、更新緩存的查詢流程

舊流程 新流程
1. 根據(jù)TicketCodes,查詢緩存,并將結(jié)果加入結(jié)果集。
2. 根據(jù)TicketCodes和結(jié)果集,找出不存在的TicketCode,然后到db查詢,將結(jié)果加入結(jié)果集,并寫(xiě)入緩存。
3. 返回結(jié)果集合。
1. 根據(jù)TicketCodes,查詢緩存,并將結(jié)果加入結(jié)果集。
2. 根據(jù)TicketCodes和結(jié)果集,找出不存在的TicketCode,然后到db查詢,將結(jié)果加入結(jié)果集,并寫(xiě)入緩存。
3. 根據(jù)TicketCodes和結(jié)果集,再次不存在的TicketCode,為這些code創(chuàng)建NullObject,并寫(xiě)入緩存。
4. 過(guò)濾結(jié)果集,剔除NullObject。
5. 返回結(jié)果集。

對(duì)比新老流程,新流程增加了步驟3、4。
步驟3的作用:為不存在的TicketCode,創(chuàng)建NullObject,寫(xiě)入緩存。下次再次查詢時(shí),緩存就有數(shù)據(jù),DB也不會(huì)查詢。
步驟4的作用:NullObject是可以正常被反序列化,但對(duì)調(diào)用方來(lái)說(shuō)是無(wú)效的數(shù)據(jù),需要剔除。

總結(jié)

帶來(lái)的好處:

  1. 如果想讓Sku,Product避免緩存穿透的問(wèn)題,只要繼承NullObject接口,就能復(fù)用已有的成果。
  2. 在redis反序列化過(guò)程,沒(méi)有特殊處理。
  3. 在原有的查詢流程追加行為,沒(méi)有破壞性的改造,成本低。

與老方案的差異:
序列化NullObject產(chǎn)生的字符串,攜帶的信息量遠(yuǎn)大于特殊字符傳,而且反序列化后,可以判斷是否為NullObject。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,868評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,754評(píng)論 18 399
  • 這部分主要是開(kāi)源Java EE框架方面的內(nèi)容,包括Hibernate、MyBatis、Spring、Spring ...
    雜貨鋪老板閱讀 1,417評(píng)論 0 2
  • 第五章 他的真面貌 樓柒是被一陣清脆的鳥(niǎo)鳴叫醒的。 剛開(kāi)始她有點(diǎn)兒迷迷糊糊不知今夕何夕,還以為自己在臭老道的道觀里...
    a63df4c3d8c0閱讀 865評(píng)論 1 1
  • 今天天氣又變冷了,不是很懂南京。 因?yàn)橹苣┑淖晕以u(píng)價(jià)受到了同事的鼓勵(lì)(?)所以今天就比較努力在干活,而且內(nèi)心終歸是...
    真晝之月閱讀 129評(píng)論 0 0