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

1、為什么要用緩存

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

2、緩存的缺點

雖然緩存可以大大提高服務的效率,但是他也有不少缺點。

首先,緩存一般第一次訪問的時候還是會比較慢,因為他需要初始化,除非事先預知用戶會訪問哪些數(shù)據(jù),事先緩存。

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

最后,緩存增加了編程的復雜性,我們不僅需要考慮如何設置緩存,還需要考慮如何清除緩存,往往我們看到的很多異常數(shù)據(jù),經(jīng)常都是因為緩存沒有正確更新導致的。

3、緩存的類型

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

4、Spring Cache

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

4.1注解緩存

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

我們以CmsController為例,對getCategoryTree方法進行緩存,因為我們的分類樹很少發(fā)生改變

這里我們使用了@Cacheable注解。我們在方法內(nèi)部打個斷點,我們訪問頁面,第一次進入斷點,刷新界面,發(fā)現(xiàn)不會再進入斷點,說明我們的緩存生效了。

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

4.2編程緩存

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

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

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

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

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

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

5、定時清除

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

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

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

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

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

6、Redis緩存

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

7、總結

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

代碼:

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

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

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