0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

盤點(diǎn)那些強(qiáng)大又低調(diào)的Java緩存

jf_ro2CN3Fa ? 來源:勇哥java實(shí)戰(zhàn)分享 ? 2023-11-14 18:02 ? 次閱讀

這篇文章,筆者想聊聊那些在業(yè)務(wù)系統(tǒng)中較少被使用,但卻活躍于中間件或者框架里,強(qiáng)大卻又低調(diào)的緩存,筆者愿稱他們?yōu)榫彺媸澜绲膾叩厣?/p>

88a477a0-82d4-11ee-939d-92fbcf53809c.png

1 HashMap/ConcurrentHashMap 配置緩存

HashMap 是一種基于哈希表的集合類,它提供了快速的插入、查找和刪除操作。

HashMap 是很多程序員接觸的第一種緩存 , 因?yàn)楝F(xiàn)實(shí)業(yè)務(wù)場(chǎng)景里,我們可能需要給緩存添加緩存統(tǒng)計(jì)、過期失效、淘汰策略等功能,HashMap 的功能就顯得孱弱 ,所以 HashMap 在業(yè)務(wù)系統(tǒng)中使用得并不算多。

但 HashMap 在中間件中卻是香餑餑,我們消息中間件 RocketMQ 為例。

88b1137a-82d4-11ee-939d-92fbcf53809c.jpg

上圖是 RocketMQ 的集群模式 ,Broker 分為 Master 與 Slave,一個(gè) Master 可以對(duì)應(yīng)多個(gè) Slave,但是一個(gè) Slave 只能對(duì)應(yīng)一個(gè) Master。

每個(gè) Broker 與 Name Server 集群中的所有節(jié)點(diǎn)建立長(zhǎng)連接,定時(shí)每隔 30 秒注冊(cè) 主題的路由信息到所有 Name Server。

消息發(fā)送者、消息消費(fèi)者,在同一時(shí)間只會(huì)連接 Name Server 集群中的一臺(tái)服務(wù)器,并且會(huì)每隔 30s 會(huì)定時(shí)更新 Topic 的路由信息。

我們可以理解 Name Server 集群的作用就是注冊(cè)中心,注冊(cè)中心會(huì)保存路由信息(主題的讀寫隊(duì)列數(shù)、操作權(quán)限等),路由信息就是保存在 HashMap 中 。

88c56532-82d4-11ee-939d-92fbcf53809c.jpg

路由信息通過幾個(gè) HashMap 來保存,當(dāng) Broker 向 Nameserver 發(fā)送心跳包(路由信息),Nameserver 需要對(duì) HashMap 進(jìn)行數(shù)據(jù)更新,但我們都知道 HashMap 并不是線程安全的,高并發(fā)場(chǎng)景下,容易出現(xiàn) CPU 100% 問題,所以更新 HashMap 時(shí)需要加鎖,RocketMQ 使用了 JDK 的讀寫鎖 ReentrantReadWriteLock 。

下面我們看下路由信息如何更新和讀?。?/p>

1、寫操作:更新路由信息,操作寫鎖

88d51266-82d4-11ee-939d-92fbcf53809c.jpg

2、讀操作:查詢主題信息,操作讀鎖

88e026a6-82d4-11ee-939d-92fbcf53809c.jpg

同時(shí),我們需要注意 Name Server 維護(hù)路由信息還需要定時(shí)任務(wù)的支撐。

每個(gè) Broker 定時(shí)每隔 30 秒注冊(cè) 主題的路由信息到所有 Name Server

Name Server 定時(shí)任務(wù)每隔10 秒清除已宕機(jī)的 Broker

我們做一個(gè)小小的總結(jié),Name Server 維護(hù)路由的模式是:HashMap + 讀寫鎖 + 定時(shí)任務(wù)更新。

HashMap 作為存儲(chǔ)容器

讀寫鎖控制鎖的顆粒度

定時(shí)任務(wù)定時(shí)更新緩存

寫到這里,我們不禁想到 ConcurrentHashMap。

ConcurrentHashMap 可以保證線程安全,JDK1.7 之前使用分段鎖機(jī)制實(shí)現(xiàn),JDK1.8 則使用數(shù)組+鏈表+紅黑樹數(shù)據(jù)結(jié)構(gòu)和CAS原子操作實(shí)現(xiàn)。

Broker 使用不同的 ConcurrentHashMap 分別用來存儲(chǔ)消費(fèi)組、消費(fèi)進(jìn)度、消息過濾信息等。

那么名字服務(wù)為什么不使用 ConcurrentHashMap 作為存儲(chǔ)容器呢 ?

最核心的原因在于:路由信息由多個(gè) HashMap 組成,通過每次寫操作可能要操作多個(gè)對(duì)象 ,為了保證其一致性,所以才需要加讀寫鎖。

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro

視頻教程:https://doc.iocoder.cn/video/

2 LinkedHashMap 最近最少使用緩存

LinkedHashMap 是 HashMap 的子類,但是內(nèi)部還有一個(gè)雙向鏈表維護(hù)鍵值對(duì)的順序,每個(gè)鍵值對(duì)既位于哈希表中,也位于雙向鏈表中。

LinkedHashMap 支持兩種順序插入順序 、 訪問順序。

插入順序:先添加的在前面,后添加的在后面,修改操作并不影響順序

訪問順序:?jiǎn)栔傅氖?get/put 操作,對(duì)一個(gè)鍵執(zhí)行 get/put 操作后,其對(duì)應(yīng)的鍵值對(duì)會(huì)移動(dòng)到鏈表末尾,所以最末尾的是最近訪問的,最開始的是最久沒有被訪問的,這就是訪問順序。

LinkedHashMap 經(jīng)典的用法是作為 LruCache (最近最少使用緩存) ,而 MyBatis 的二級(jí)緩存的淘汰機(jī)制就是使用的 LinkedHashMap 。

MyBatis 的二級(jí)緩存是使用責(zé)任鏈+ 裝飾器的設(shè)計(jì)模式實(shí)現(xiàn)的。

88e74e0e-82d4-11ee-939d-92fbcf53809c.jpg

上圖中,裝飾器包目錄下 Cache 接口有不同的實(shí)現(xiàn)類,比如過期淘汰、日志記錄等。

88ef3088-82d4-11ee-939d-92fbcf53809c.jpg

LruCache 使用了裝飾器模式 ,使用 LinkedHashMap 默認(rèn)保存 1024 個(gè)緩存 key ,當(dāng) key 最久未被訪問,并且 keyMap 的大小超過 1024 時(shí) ,記錄最老的 key ,當(dāng)下次添加緩存對(duì)象時(shí),刪除最老的 key。

使用 LinkedHashMap 重點(diǎn)需要做到使用訪問順序模式和重寫 removeEldestEntry 方法。因?yàn)?LinkedHashMap 并不是線程安全的,Mybatis 二級(jí)緩存責(zé)任鏈中 SynchronizedCache 對(duì)象可以實(shí)現(xiàn)線程安全的對(duì)緩存讀寫。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

項(xiàng)目地址:https://github.com/YunaiV/yudao-cloud

視頻教程:https://doc.iocoder.cn/video/

3 TreeMap 排序?qū)ο缶彺?/p>

TreeMap 是一種基于紅黑樹的有序 Map,它可以按照鍵的順序進(jìn)行遍歷。

TreeMap 有兩種應(yīng)用場(chǎng)景讓筆者印象極為深刻 ,他們分別是一致性哈希算法和 RocketMQ 消費(fèi)快照 。

下面重點(diǎn)介紹 TreeMap 在一致性哈希算法中的應(yīng)用。

一致性哈希(Consistent Hashing)算法被廣泛應(yīng)用于緩存系統(tǒng)、分布式數(shù)據(jù)庫(kù)、負(fù)載均衡器等分布式系統(tǒng)中,以實(shí)現(xiàn)高性能和高可用性。它解決了傳統(tǒng)哈希算法在動(dòng)態(tài)環(huán)境下擴(kuò)展性和負(fù)載均衡性能的問題。

一致性哈希的主要優(yōu)點(diǎn)是在節(jié)點(diǎn)增減時(shí),只有少量的數(shù)據(jù)需要重新映射,因?yàn)橹挥心切┲苯踊蜷g接與新增或刪除節(jié)點(diǎn)相鄰的數(shù)據(jù)項(xiàng)需要遷移。這大大減少了系統(tǒng)的遷移開銷和影響,使得系統(tǒng)更具擴(kuò)展性和可伸縮性。

TreeMap 在一致性哈希中可以用作節(jié)點(diǎn)/虛擬節(jié)點(diǎn)的存儲(chǔ)結(jié)構(gòu),用來維護(hù)節(jié)點(diǎn)在哈希環(huán)上的位置和鍵的有序性。

1、我們定義一個(gè) TreeMap 存儲(chǔ)節(jié)點(diǎn)/虛擬節(jié)點(diǎn) 。

890959fe-82d4-11ee-939d-92fbcf53809c.png

2、初始化節(jié)點(diǎn)

構(gòu)造函數(shù)包含三個(gè)部分:物理節(jié)點(diǎn)集合、每個(gè)物理節(jié)點(diǎn)對(duì)應(yīng)的虛擬節(jié)點(diǎn)個(gè)數(shù)、哈希函數(shù) 。

89146614-82d4-11ee-939d-92fbcf53809c.png

我們重點(diǎn)看下添加節(jié)點(diǎn)邏輯:

891dd2e4-82d4-11ee-939d-92fbcf53809c.png

3、按照 key 查詢節(jié)點(diǎn)

添加完節(jié)點(diǎn)之后,節(jié)點(diǎn)分布類似下圖:

8935c548-82d4-11ee-939d-92fbcf53809c.jpg

893fe244-82d4-11ee-939d-92fbcf53809c.png

當(dāng)需要定位某個(gè) key 屬于哪個(gè)節(jié)點(diǎn)時(shí),先通過哈希函數(shù)計(jì)算 key 的哈希值,并在環(huán)上順時(shí)針方向找到第一個(gè)大于等于該哈希值的節(jié)點(diǎn)位置。該節(jié)點(diǎn)即為數(shù)據(jù)的歸屬節(jié)點(diǎn) 。

我們添加一個(gè)新的節(jié)點(diǎn) node5 , 從下圖中,我們可以看到,影響的范圍(深黃色)并不大 ,這也就是一致性哈希算法的優(yōu)勢(shì)。

8956c536-82d4-11ee-939d-92fbcf53809c.jpg

4 ByteBuffer 網(wǎng)絡(luò)編程緩沖池

ByteBuffer 是字節(jié)緩沖區(qū),主要用于用戶讀取和緩存字節(jié)數(shù)據(jù),多用于網(wǎng)絡(luò)編程、文件 IO 處理等。

筆者第一次接觸 ByteBuffer 是在分庫(kù)分表中間件 Cobar 中 。在網(wǎng)絡(luò)編程里,經(jīng)常需要分配內(nèi)存,在高并發(fā)場(chǎng)景下,性能壓力比較大。

Cobar 抽象了一個(gè) NIOProcessor 類用來處理網(wǎng)絡(luò)請(qǐng)求,每個(gè)處理器初始化的時(shí)候都會(huì)創(chuàng)建一個(gè)緩沖池 BufferPool 。BufferPool 用于池化 ByteBuffer ,這和我們平常使用的數(shù)據(jù)庫(kù)連接池的思路是一致的。

8964ec9c-82d4-11ee-939d-92fbcf53809c.png

下圖展示了緩沖池 BufferPool 的源碼:

896fb83e-82d4-11ee-939d-92fbcf53809c.png

緩沖池 BufferPool 的核心功能是分配緩存和回收緩存 ,通過將緩存池化,可以大大提升系統(tǒng)的性能。

如今 ,Netty 內(nèi)置了更為強(qiáng)大的內(nèi)存池化工具 ByteBuf ,我們會(huì)在后面的文章里詳聊。

5 寫到最后

這篇文章,筆者總結(jié)了四種強(qiáng)大且低調(diào)的緩存。

1、HashMap/ConcurrentHashMap 經(jīng)常用于配置緩存,對(duì)于 HashMap 來講,HashMap + 讀寫鎖 + 定時(shí)任務(wù)更新是常用的模式。而 ConcurrentHashMap 廣泛存在于各種中間件,線程安全且靈活易用。

2、LinkedHashMap 經(jīng)常被用于創(chuàng)建最近最少使用緩存 LruCache 。推薦學(xué)習(xí) Mybatis 二級(jí)緩存的設(shè)計(jì),它使用責(zé)任鏈+ 裝飾器的設(shè)計(jì)模式,內(nèi)置 LruCache 的實(shí)現(xiàn)就是使用 LinkedHashMap 。

3、TreeMap 是一種基于紅黑樹的有序 Map 。TreeMap 在一致性哈希中可以用作節(jié)點(diǎn)/虛擬節(jié)點(diǎn)的存儲(chǔ)結(jié)構(gòu),用來維護(hù)節(jié)點(diǎn)在哈希環(huán)上的位置和鍵的有序性。

4、ByteBuffer 是字節(jié)緩沖區(qū),主要用于用戶讀取和緩存字節(jié)數(shù)據(jù),多用于網(wǎng)絡(luò)編程、文件 IO 處理等。分庫(kù)分表中間件 Cobar 在網(wǎng)絡(luò)請(qǐng)求處理中,創(chuàng)建了緩沖池 BufferPool 用于池化 ByteBuffer ,從而大大提升系統(tǒng)的性能。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2947

    瀏覽量

    104374
  • 緩存
    +關(guān)注

    關(guān)注

    1

    文章

    228

    瀏覽量

    26610
  • 程序員
    +關(guān)注

    關(guān)注

    4

    文章

    947

    瀏覽量

    29734

原文標(biāo)題:盤點(diǎn)那些強(qiáng)大又低調(diào)的 Java 緩存

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    如何選擇合適的本地緩存?

    的 Guava 緩存、在 Guava 上進(jìn)一步傳承的 Caffine 以及自稱在 Java 中使用最廣泛的 EhCache,那么我們?cè)撛趺催x擇適合自己應(yīng)用的緩存呢,小編下面會(huì)簡(jiǎn)單介紹,并將以上
    的頭像 發(fā)表于 01-18 11:19 ?777次閱讀
    如何選擇合適的本地<b class='flag-5'>緩存</b>?

    基于Java的分布式緩存優(yōu)化在網(wǎng)絡(luò)管理系統(tǒng)中的應(yīng)用

    基于Java的分布式緩存優(yōu)化在網(wǎng)絡(luò)管理系統(tǒng)中的應(yīng)用討論建立在JMX管理框架上的網(wǎng)絡(luò)性能管理系統(tǒng)的優(yōu)化方案,利用JMX體系結(jié)構(gòu)的可擴(kuò)展特性,在系統(tǒng)的服務(wù)器端嵌入了分布式緩存系統(tǒng)對(duì)服務(wù)器端的緩存
    發(fā)表于 09-19 09:20

    ASP緩存技術(shù)

    緩存中時(shí),再次查詢時(shí)耗費(fèi)的時(shí)間主要是在顯示數(shù)據(jù)的時(shí)間上了。也就是說,我們不應(yīng)該把經(jīng)常需要改變的數(shù)據(jù)放到服務(wù)端的緩存中,我們應(yīng)該把改變少,但是需要經(jīng)常訪問的數(shù)據(jù)放到緩存中?,F(xiàn)在我們先
    發(fā)表于 11-21 10:53

    Java中的輸入輸出流盤點(diǎn)

    Java中的流分為兩種,一種是字節(jié)流,另一種是字符流,分別由四個(gè)抽象類來表示(每種流包括輸入和輸出兩種所以一共四個(gè)):InputStream,OutputStream,Reader,Writer。Java中其他多種多樣變化的流均是由它們派生出來的。
    發(fā)表于 07-11 07:56

    Java程序員筆記之mybatis結(jié)合redis實(shí)戰(zhàn)二級(jí)緩存

    Java程序員筆記——mybatis結(jié)合redis實(shí)戰(zhàn)二級(jí)緩存
    發(fā)表于 06-10 09:15

    Java 使用Redis緩存工具的詳細(xì)解說

    本文是關(guān)于Java 使用Redis緩存工具的詳細(xì)解說。詳細(xì)步驟請(qǐng)看下文
    的頭像 發(fā)表于 02-09 14:10 ?7834次閱讀
    <b class='flag-5'>Java</b> 使用Redis<b class='flag-5'>緩存</b>工具的詳細(xì)解說

    TicWatchPro體驗(yàn) 獨(dú)立強(qiáng)大的智能手表

    智能可穿戴設(shè)備最早只存在于科幻電影中,無論是項(xiàng)鏈還是手表,功能強(qiáng)大百變的“特工用具”讓我們興奮不已。都說科幻電影在一定程度上反映、引領(lǐng)了未來科技的發(fā)展,我們也不停的在問,那些電影中炫酷的黑科技何時(shí)才能真正“飛入尋常百姓家”呢?
    的頭像 發(fā)表于 01-21 10:34 ?3042次閱讀

    盤點(diǎn)嵌入式的那些常見GUI:emWin、TouchGFX、MiniGUI、Qt等

    盤點(diǎn)嵌入式那些常見的GUI:emWin、TouchGFX、MiniGUI、Qt等
    的頭像 發(fā)表于 02-05 12:38 ?9469次閱讀

    盤點(diǎn)CES2020上的那些機(jī)器人

    美國(guó)消費(fèi)電子展CES 2020接近尾聲,這次展會(huì)上機(jī)器人依舊是一個(gè)吸引大眾目光的領(lǐng)域。技術(shù)的不斷革新,讓機(jī)器人或者機(jī)械骨骼們開始應(yīng)用到各種領(lǐng)域之中。下面,就讓我們來為盤點(diǎn)一下本屆CES上那些閃耀全場(chǎng)的機(jī)器人吧。
    的頭像 發(fā)表于 01-10 17:54 ?3308次閱讀

    Redis集群緩存方案,緩存常見問題盤點(diǎn)

    如今,緩存系統(tǒng)的應(yīng)用非常廣泛,能夠用來提高并發(fā)數(shù)、數(shù)據(jù)吞吐量,提高快速響應(yīng)能力。那么當(dāng)數(shù)據(jù)量達(dá)到一定程度,單機(jī)環(huán)境可能就顯得有些力不從心了,就需要一個(gè)分布式緩存系統(tǒng)。
    發(fā)表于 12-16 10:48 ?2248次閱讀
    Redis集群<b class='flag-5'>緩存</b>方案,<b class='flag-5'>緩存</b>常見問題<b class='flag-5'>盤點(diǎn)</b>

    關(guān)于內(nèi)存緩存那些

    而包含和不包含的區(qū)別在這里就會(huì)有所體現(xiàn)。如果是包含策略,那么新數(shù)據(jù)直接覆蓋舊數(shù)據(jù)即可,舊數(shù)據(jù)等于直接作廢,除非這個(gè)數(shù)據(jù)最近在CPU中被改寫過,需要返回到內(nèi)存中進(jìn)行保存,那么才需要將該緩存行刷回內(nèi)存(那么如何確定該緩存行是否被改寫過呢?可以用一個(gè)名為dirty的標(biāo)志位注明)
    的頭像 發(fā)表于 08-03 16:59 ?2864次閱讀
    關(guān)于內(nèi)存<b class='flag-5'>緩存</b>的<b class='flag-5'>那些</b>事

    介紹下cpu緩存一致性(MESI協(xié)議)

    之前介紹了java并發(fā)包的cas原理和java內(nèi)存模型,這篇我們介紹下cpu緩存一致性原理,可以幫助我們更好的理解cas的底層原理。
    的頭像 發(fā)表于 06-09 16:01 ?4463次閱讀
    介紹下cpu<b class='flag-5'>緩存</b>一致性(MESI協(xié)議)

    Ehcache!這才是Java本地緩存之王!

    Java而言,其常用的緩存解決方案有很多,例如數(shù)據(jù)庫(kù)緩存框架EhCache,分布式緩存Memcached等,這些緩存方案實(shí)際上都是為了提升
    的頭像 發(fā)表于 07-29 11:21 ?1590次閱讀
    Ehcache!這才是<b class='flag-5'>Java</b>本地<b class='flag-5'>緩存</b>之王!

    基于java開發(fā)的緩存框架jetcache簡(jiǎn)介

    在實(shí)際應(yīng)用中,并不是單一的使用本地緩存或者redis,更多是組合使用來滿足不同的業(yè)務(wù)場(chǎng)景,于是如何優(yōu)雅的組合本地緩存和遠(yuǎn)程緩存就成了我們要研究的問題,而這一點(diǎn),阿里開源的jetcache組件幫我們實(shí)現(xiàn)了
    的頭像 發(fā)表于 09-07 10:36 ?1585次閱讀
    基于<b class='flag-5'>java</b>開發(fā)的<b class='flag-5'>緩存</b>框架jetcache簡(jiǎn)介

    CPU緩存那些事兒

    CPU Cache 在讀取內(nèi)存數(shù)據(jù)時(shí),每次不會(huì)只讀一個(gè)字或一個(gè)字節(jié),而是一塊塊地讀取,這每一小塊數(shù)據(jù)也叫CPU 緩存行(CPU Cache Line)。這也是對(duì)局部性原理的運(yùn)用,當(dāng)一個(gè)指令或數(shù)據(jù)被
    的頭像 發(fā)表于 09-10 10:57 ?622次閱讀
    CPU<b class='flag-5'>緩存</b><b class='flag-5'>那些</b>事兒