Java Spring三級(jí)緩存
三級(jí)緩存是指什么
我們常說(shuō)的三級(jí)緩存如下:
- CPU三級(jí)緩存
- Spring三級(jí)緩存
- 應(yīng)用架構(gòu)(JVM、分布式緩存、db)三級(jí)緩存
CPU
基本概念
CPU 的訪問(wèn)速度每 18 個(gè)月就會(huì)翻 倍,相當(dāng)于每年增? 60% 左右,內(nèi)存的速度當(dāng)然也會(huì)不斷增?,但是增?的速度遠(yuǎn)小于 CPU,平均每年 只增? 7% 左右。于是,CPU 與內(nèi)存的訪問(wèn)性能的差距不斷拉大。
為了彌補(bǔ) CPU 與內(nèi)存兩者之間的性能差異,就在 CPU 內(nèi)部引入了 CPU Cache,也稱高速緩存。
CPU Cache 通常分為大小不等的三級(jí)緩存,分別是 L1 Cache、L2 Cache 和 L3 Cache。其中L3是多個(gè)核心共享的。
離 CPU 核心越近,緩存的讀寫(xiě)速度就越快
但 CPU 的空間很狹小,離 CPU 越近緩存大小受到的限制也越大。
所以,綜合硬件布局、性能等因素,CPU 緩存通常分為大小不等的三級(jí)緩存。
三級(jí)緩存要比一、二級(jí)緩存大許多倍,這是因?yàn)楫?dāng)下的 CPU 都是多核心的,每個(gè)核心都有自己的一、二級(jí)緩存,- 但
三級(jí)緩存卻是一顆 CPU 上所有核心共享的 緩存一致性:在多核CPU時(shí)代,CPU有“緩存一致性”原則,也就是說(shuō)每個(gè)處理器(核)都會(huì)通過(guò)嗅探在總線上傳播的數(shù)據(jù)來(lái)檢查自己的緩存值是不是過(guò)期了。如果過(guò)期了,則失效。- 比如聲明volitate,當(dāng)變量被修改,則會(huì)立即要求寫(xiě)入系統(tǒng)內(nèi)存。
程序執(zhí)行數(shù)據(jù)流向
順序如下
- 先將內(nèi)存中的數(shù)據(jù)加載到共享的 L3 Cache 中,
- 再加載到每個(gè)核心獨(dú)有的 L2 Cache,
- 最后 進(jìn)入到最快的 L1 Cache,之后才會(huì)被 CPU 讀取。
- 之間的層級(jí)關(guān)系,如下圖。

Spring三級(jí)緩存
概述
三級(jí)緩存就是在Bean生成流程中保存Bean對(duì)象三種形態(tài)的三個(gè)Map集合
]
這個(gè)三級(jí)緩存就是為了解決循環(huán)依賴
當(dāng)創(chuàng)建相互依賴的對(duì)象時(shí),會(huì)形成死循環(huán),例如下圖無(wú)緩存中的情況。

- 而Spring通過(guò)增加緩存,
將未完全創(chuàng)建好的A提前暴露在緩存中,當(dāng)相互依賴的對(duì)象B對(duì)屬性A賦值時(shí),可以直接從緩存中獲取A,而不需要再創(chuàng)建A。如下所示
哪三個(gè)緩存
Spring三級(jí)緩存機(jī)制包括以下三個(gè)緩存:
singletonObjects:一級(jí)緩存,緩存中的bean是已經(jīng)創(chuàng)建完成的,該bean經(jīng)歷過(guò)實(shí)例化->屬性填充->初始化以及各種的后置處理。因此,一旦需要獲取bean時(shí),會(huì)優(yōu)先尋找一級(jí)緩存- ???????
earlySingletonObjects:二級(jí)緩存,該緩存跟一級(jí)緩存的區(qū)別在于,該緩存所獲取到的bean是提前曝光出來(lái)的,是還沒(méi)創(chuàng)建完成的。也就是說(shuō)獲取到的bean只能確保已經(jīng)進(jìn)行了實(shí)例化,但是屬性填充跟初始化還沒(méi)有做完,因此該bean還沒(méi)創(chuàng)建完成,時(shí)半成品,僅僅能作為指針提前曝光,被其他bean所引用 singletonFactories:三級(jí)緩存,在bean實(shí)例化完之后,屬性填充以及初始化之前,如果允許提前曝光,spring會(huì)將實(shí)例化后的bean提前曝光,也就是把該bean轉(zhuǎn)換成beanFactory并加入到三級(jí)緩存。在需要引用提前曝光對(duì)象時(shí)再通過(guò)singletonFactory.getObject()獲取。
// 一級(jí)緩存Map 存放完整的Bean(流程跑完的) private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); // 二級(jí)緩存Map 存放不完整的Bean(只實(shí)例化完,還沒(méi)屬性賦值、初始化) private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16); // 三級(jí)緩存Map 存放一個(gè)Bean的lambda表達(dá)式(也是剛實(shí)例化完) private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
發(fā)現(xiàn)兩個(gè)Bean循環(huán)依賴時(shí)
當(dāng)Spring發(fā)現(xiàn)兩個(gè)或更多個(gè)bean之間存在循環(huán)依賴關(guān)系時(shí)
- 它會(huì)先將其中一個(gè)beanA創(chuàng)建的過(guò)程中尚未完成的實(shí)例放入earlySingletonObjects緩存中,
- 然后將創(chuàng)建該beanA的工廠對(duì)象放入singletonFactories緩存中。
- 接著,Spring會(huì)暫停當(dāng)前bean的創(chuàng)建過(guò)程,去創(chuàng)建它所依賴的bean。
- 當(dāng)依賴的bean創(chuàng)建完成后,Spring會(huì)將其放入singletonObjects緩存中,并使用它來(lái)完成當(dāng)前bean的創(chuàng)建過(guò)程。
- 在創(chuàng)建當(dāng)前bean的過(guò)程中,如果發(fā)現(xiàn)它還依賴其他的bean,Spring會(huì)重復(fù)上述過(guò)程,直到所有bean的創(chuàng)建過(guò)程都完成為止。
- 注意:當(dāng)使用構(gòu)造函數(shù)注入方式時(shí),循環(huán)依賴是無(wú)法解決的。
- 因?yàn)樵趧?chuàng)建bean時(shí),必須先創(chuàng)建它所依賴的bean實(shí)例,而構(gòu)造函數(shù)注入方式需要在創(chuàng)建bean實(shí)例時(shí)就將依賴的bean實(shí)例傳入構(gòu)造函數(shù)中。
- 如果依賴的bean實(shí)例尚未創(chuàng)建完成,就無(wú)法將其傳入構(gòu)造函數(shù)中,從而導(dǎo)致循環(huán)依賴無(wú)法解決。
- 此時(shí),可以考慮使用setter注入方式來(lái)解決循環(huán)依賴問(wèn)題。
當(dāng)A和B相互依賴時(shí),若先創(chuàng)建實(shí)例A,則整個(gè)調(diào)用過(guò)程如下:

簡(jiǎn)化圖如下
應(yīng)用架構(gòu)三級(jí)緩存
概述
應(yīng)用架構(gòu)三級(jí)緩存的時(shí)候,一般說(shuō)JVM級(jí)別的、分布式緩存級(jí)別的、數(shù)據(jù)庫(kù)級(jí)別的
JVM級(jí)別:一般常見(jiàn)本地緩存框架有Guava Cache和Caffeine Cache分布式緩存級(jí)別:一般用的Redis。數(shù)據(jù)庫(kù)級(jí)別:mysql等數(shù)據(jù)庫(kù)
眾所周知 MySQL 數(shù)據(jù)庫(kù)會(huì)將數(shù)據(jù)存儲(chǔ)在硬盤以防止掉電丟失,但是受制于硬盤的物理設(shè)計(jì),即便是目前性能最好的企業(yè)級(jí) SSD 硬盤,也比內(nèi)存的這種高速設(shè)備 IO 層面差一個(gè)數(shù)量級(jí)
典型的 “讀多寫(xiě)少” 的場(chǎng)景,需要在設(shè)計(jì)上進(jìn)行數(shù)據(jù)的讀寫(xiě)分離,數(shù)據(jù)寫(xiě)入時(shí)直接落盤處理,
而占比超過(guò) 90% 的數(shù)據(jù)讀取操作時(shí)則從以 Redis 為代表的內(nèi)存 NoSQL 數(shù)據(jù)庫(kù)提取數(shù)據(jù),利用內(nèi)存的高吞吐瞬間完成數(shù)據(jù)提取,這里 Redis 的作用就是我們常說(shuō)的緩存。
二級(jí)緩存架構(gòu)
二級(jí)緩存架構(gòu)
1級(jí)為本地緩存,或者進(jìn)程內(nèi)的緩存(如 Ehcache) ——速度快,進(jìn)程內(nèi)可用- ???????
2級(jí)為集中式緩存(如 Redis)——可同時(shí)為多節(jié)點(diǎn)提供服務(wù)
?????????????? Java 的應(yīng)用端多級(jí)緩存
- 在 Java 的應(yīng)用端也要設(shè)計(jì)多級(jí)緩存,我們
將進(jìn)程內(nèi)緩存與分布式緩存服務(wù)結(jié)合,有效分?jǐn)倯?yīng)用壓力。 - 在
Java 應(yīng)用層面,只有 本地緩存(EhCache、Caffeine Cache) 的緩存不存在時(shí),再去 Redis 分布式緩存獲取, - 如果 Redis 也沒(méi)有此數(shù)據(jù)再去數(shù)據(jù)庫(kù)查詢,
數(shù)據(jù)查詢成功后對(duì) Redis 與 本地緩存 同時(shí)進(jìn)行雙寫(xiě)更新。 - 這樣 Java 應(yīng)用下一次再查詢相同數(shù)據(jù)時(shí)便直接從本地緩存提取,不再產(chǎn)生新的網(wǎng)絡(luò)通信,應(yīng)用查詢性能得到顯著提高。
為了保證緩存一致性,利用 通知(MQ、發(fā)布訂閱模式等) 向其他服務(wù)實(shí)例以及 Redis 緩存服務(wù)發(fā)起變更通知。
到此這篇關(guān)于三級(jí)緩存的文章就介紹到這了,更多相關(guān)三級(jí)緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java int類型二維數(shù)組實(shí)現(xiàn)“楊輝三角”的完整實(shí)例
這篇文章主要給大家介紹了關(guān)于java int類型二維數(shù)組實(shí)現(xiàn)“楊輝三角”的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Intellij無(wú)法創(chuàng)建java文件解決方案
這篇文章主要介紹了Intellij無(wú)法創(chuàng)建java文件解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
淺談Java垃圾回收的實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了淺談Java垃圾回收的實(shí)現(xiàn)過(guò)程,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12
Java中使用Hutool的DsFactory操作多數(shù)據(jù)源的實(shí)現(xiàn)
在Java開(kāi)發(fā)中,管理多個(gè)數(shù)據(jù)源是一項(xiàng)常見(jiàn)需求,Hutool作為一個(gè)全能的Java工具類庫(kù),提供了DsFactory工具,幫助開(kāi)發(fā)者便捷地操作多數(shù)據(jù)源,感興趣的可以了解一下2024-09-09
Java中常見(jiàn)的XML解析方法與應(yīng)用詳解
XML(eXtensible Markup Language)是一種用于存儲(chǔ)和傳輸數(shù)據(jù)的標(biāo)記語(yǔ)言,被廣泛應(yīng)用于表示和交換獨(dú)立于應(yīng)用程序和硬件平臺(tái)的結(jié)構(gòu)化信息,下面我們就來(lái)看看它的常見(jiàn)解析方法有哪些吧2024-01-01
Spring 中的 @PathVariable 注解及應(yīng)用場(chǎng)景分析
@PathVariable注解是 Spring 框架中一個(gè)非常實(shí)用的注解,它可以幫助我們輕松地從 URL 中提取參數(shù),從而實(shí)現(xiàn) RESTful API 的開(kāi)發(fā),通過(guò)本文的介紹,我們了解了@PathVariable注解的基本使用方法和高級(jí)用法,以及它的應(yīng)用場(chǎng)景,感興趣的朋友跟隨小編一起看看吧2025-05-05
spring boot使用自定義的線程池執(zhí)行Async任務(wù)
這篇文章主要介紹了spring boot使用自定義的線程池執(zhí)行Async任務(wù)的相關(guān)資料,需要的朋友可以參考下2018-02-02
如何解決java:錯(cuò)誤:無(wú)效的源發(fā)行版:17問(wèn)題
這篇文章主要介紹了如何解決java:錯(cuò)誤:無(wú)效的源發(fā)行版:17問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
springboot 使用poi進(jìn)行數(shù)據(jù)的導(dǎo)出過(guò)程詳解
這篇文章主要介紹了springboot 使用poi進(jìn)行數(shù)據(jù)的導(dǎo)出過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09

