Spring Boot入門(第三十四章):緩存

1、為什么要用緩存

現(xiàn)在我們系統(tǒng)里面的數(shù)據(jù)還比較少,所以頁(yè)面看起來還是比較快,但是隨著數(shù)據(jù)量的增長(zhǎng),我們很快就會(huì)發(fā)現(xiàn)頁(yè)面會(huì)越來越慢,這時(shí)我們就需要進(jìn)行優(yōu)化。之前我們很多接口服務(wù)做了多次查詢,一個(gè)服務(wù)執(zhí)行多次sql,當(dāng)然會(huì)慢,大部分情況下盡量我們最好執(zhí)行一次sql,但是執(zhí)行一次sql,就會(huì)增加代碼的復(fù)雜度,本來邏輯很清楚的代碼,變成了一個(gè)復(fù)雜的sql語句,這時(shí)我們可以考慮使用緩存。再者sql的優(yōu)化也是有限的,也許已經(jīng)優(yōu)化得最優(yōu)了,sql執(zhí)行還是比較長(zhǎng),這時(shí)我們用緩存可以緩存數(shù)據(jù)庫(kù)的壓力,以前需要幾秒才能返回的服務(wù),現(xiàn)在只需要毫秒級(jí)的時(shí)間。

2、緩存的缺點(diǎn)

雖然緩存可以大大提高服務(wù)的效率,但是他也有不少缺點(diǎn)。

首先,緩存一般第一次訪問的時(shí)候還是會(huì)比較慢,因?yàn)樗枰跏蓟?,除非事先預(yù)知用戶會(huì)訪問哪些數(shù)據(jù),事先緩存。

其次,緩存有時(shí)效性,緩存的數(shù)據(jù)不可能一直有效,一直有效的數(shù)據(jù)那是常量了,緩存的數(shù)據(jù)一般有兩種情況,一種是設(shè)置有效時(shí)間,超過時(shí)間自動(dòng)清除,另一種是需要開發(fā)人員編程手動(dòng)去清除,當(dāng)然可以兩者結(jié)合。如果我們緩存的數(shù)據(jù)更新非常頻繁,那么緩存就沒太大意義,除非業(yè)務(wù)可以接受數(shù)據(jù)的延遲。假如我們平均每秒鐘會(huì)發(fā)布一篇文章,我們將文章列表緩存起來,有效時(shí)間設(shè)為1秒鐘,這樣,sql每秒鐘最多執(zhí)行1次,發(fā)布的文章基本上可以實(shí)時(shí)看到,但是如果sql執(zhí)行效率比較低,1秒鐘執(zhí)行一次還是困難,我們可以將緩存設(shè)置為1分鐘,這樣1分鐘最多執(zhí)行一次,但是用戶得接受新發(fā)布的文章可能需要1分鐘以后才能看到。

最后,緩存增加了編程的復(fù)雜性,我們不僅需要考慮如何設(shè)置緩存,還需要考慮如何清除緩存,往往我們看到的很多異常數(shù)據(jù),經(jīng)常都是因?yàn)榫彺鏇]有正確更新導(dǎo)致的。

3、緩存的類型

我們有很多東西都可以用來作為緩存的容器,最簡(jiǎn)單的,我們直接使用容器的內(nèi)存就可以作為緩存,但這種情況只適合單機(jī)部署。分布式應(yīng)用時(shí),我們經(jīng)常使用Redis作為緩存介質(zhì)。其余的還有很多如Guava,Caffeine等等。

4、Spring Cache

Spring提供了緩存的通用接口,我們可以使用注解的方式,或者編程的方式操作緩存。但是由于各個(gè)緩存介質(zhì)支持的功能有所不同,所以Spring只能抽象出通用的接口。例如Redis是支持設(shè)置緩存有效時(shí)間的,而我們的內(nèi)存緩存不支持,所以Spring緩存的通用接口不能進(jìn)行有效時(shí)間設(shè)置。如果需要用到這一特性,我們可以直接使用Spring Redis的接口進(jìn)行編程。

4.1注解緩存

spring提供了幾個(gè)注解用來操作緩存。首先我們需要使用@EnableCaching打開注解緩存

我們以CmsController為例,對(duì)getCategoryTree方法進(jìn)行緩存,因?yàn)槲覀兊姆诸悩浜苌侔l(fā)生改變

這里我們使用了@Cacheable注解。我們?cè)诜椒▋?nèi)部打個(gè)斷點(diǎn),我們?cè)L問頁(yè)面,第一次進(jìn)入斷點(diǎn),刷新界面,發(fā)現(xiàn)不會(huì)再進(jìn)入斷點(diǎn),說明我們的緩存生效了。

重啟應(yīng)用后,再次訪問頁(yè)面,第一次會(huì)再次進(jìn)入斷點(diǎn),因?yàn)镾pring默認(rèn)使用的內(nèi)存介質(zhì)是Simple,即容器內(nèi)存,容器內(nèi)存會(huì)隨著應(yīng)用的停止而釋放,所以緩存也會(huì)跟著釋放。要想持久化可以選擇其他緩存,例如Redis。

4.2編程緩存

上面使用注解加了緩存,要想緩存的數(shù)據(jù)一直保持正確,我們必須在分類數(shù)據(jù)修改的時(shí)候清除緩存,清除緩存對(duì)應(yīng)注解@CacheEvict,但是我們修改分類的方法都是在基類完成的,不方便用注解控制,我們可以直接用編程的方式來控制。

我們首先去掉之前的@Cacheable注解,然后注入一個(gè)CacheManager

CacheManager就是Spring抽象出來的統(tǒng)一處理緩存的管理工具類。

從上面可以看到,以前只需一個(gè)注解就可以完成的功能,現(xiàn)在我們需要更多的代碼去處理。

同樣,現(xiàn)在我們需要在分類變更時(shí)處理緩存,

這里,我們選擇在分類數(shù)據(jù)新增,修改和刪除的時(shí)候,直接清除緩存,這樣,下次訪問的時(shí)候,就會(huì)重新查詢數(shù)據(jù)庫(kù)。

5、定時(shí)清除

不管是上面的注解緩存,還是編程緩存,因?yàn)槲覀儸F(xiàn)在緩存的時(shí)效性都是永久,為了保證緩存的正確性,我們都必須確保在數(shù)據(jù)修改時(shí)進(jìn)行緩存更新。我們必須確保所有可能導(dǎo)致數(shù)據(jù)變化的接口服務(wù)都能監(jiān)聽處理到。隨著項(xiàng)目越來越大,可能涉及數(shù)據(jù)修改的接口也越來越多,很容易漏掉。所以有時(shí)候選擇定時(shí)清除緩存可能更加簡(jiǎn)單可靠。

上面說了,緩存的時(shí)效性并不是所有緩存介質(zhì)都支持,我們的內(nèi)存緩存就不支持,但是我們還是可以通過自己編程來實(shí)現(xiàn)。例如,上面的例子,我們完全就可以建立一個(gè)定時(shí)任務(wù)來定時(shí)清理緩存。這里我們簡(jiǎn)單實(shí)現(xiàn)一個(gè)定時(shí)清除的功能。

我們?cè)贑msController添加一個(gè)構(gòu)造方法,在構(gòu)造方法里面啟動(dòng)一個(gè)線程,該線程每過1分鐘,將會(huì)自動(dòng)清除緩存,這樣基本上可以保證我們最遲1分鐘以后看到最新數(shù)據(jù)。

對(duì)于很多系統(tǒng),首頁(yè)有很多統(tǒng)計(jì)報(bào)表,報(bào)表一般業(yè)務(wù)復(fù)雜,查詢慢,如果報(bào)表的實(shí)時(shí)性要求不是那么高,我們就可以采用這種方法。但是這種方法在緩存清除后,第一次查詢還是會(huì)有點(diǎn)慢,我們可以進(jìn)一步優(yōu)化,在緩存過期前,先查詢數(shù)據(jù)。

這樣,除了容器啟動(dòng)第一次查詢慢,后面每次都會(huì)以最快的速度返回。這種方式可以理解為饑餓式,之前的為懶漢式。

6、Redis緩存

在分布式部署的時(shí)候,我們一般必須使用Redis緩存,使用Redis緩存也狠簡(jiǎn)單,我們只需要修改配置即可

7、總結(jié)

這節(jié)主要講解緩存的作用和使用方式,說到底緩存就是一個(gè)Map,篇幅有限,很多地方?jīng)]有細(xì)講,需要大家在實(shí)戰(zhàn)中理解。

代碼:

https://github.com/www15119258/springboot-study/tree/branch34

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容