日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > > 架構(gòu)師社區(qū)
[導(dǎo)讀]最近在看Mybatis的源碼,剛好看到緩存這一塊,Mybatis提供了一級(jí)緩存和二級(jí)緩存;一級(jí)緩存相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,功能比較齊全的是二級(jí)緩存,基本上滿足了一個(gè)緩存該有的功能;當(dāng)然如果拿來(lái)和專門的緩存框架如ehcache來(lái)對(duì)比可能稍有差距;本文我們將來(lái)整理一下實(shí)現(xiàn)一個(gè)本地緩存都應(yīng)該需要考慮哪些東西。

來(lái)源:ksfzhaohui |?http://dwz.win/Ws4

最近在看Mybatis的源碼,剛好看到緩存這一塊,Mybatis提供了一級(jí)緩存和二級(jí)緩存;一級(jí)緩存相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,功能比較齊全的是二級(jí)緩存,基本上滿足了一個(gè)緩存該有的功能;當(dāng)然如果拿來(lái)和專門的緩存框架如ehcache來(lái)對(duì)比可能稍有差距;本文我們將來(lái)整理一下實(shí)現(xiàn)一個(gè)本地緩存都應(yīng)該需要考慮哪些東西。

考慮點(diǎn)

考慮點(diǎn)主要在數(shù)據(jù)用何種方式存儲(chǔ),能存儲(chǔ)多少數(shù)據(jù),多余的數(shù)據(jù)如何處理等幾個(gè)點(diǎn),下面我們來(lái)詳細(xì)的介紹每個(gè)考慮點(diǎn),以及該如何去實(shí)現(xiàn);

1.數(shù)據(jù)結(jié)構(gòu)

首要考慮的就是數(shù)據(jù)該如何存儲(chǔ),用什么數(shù)據(jù)結(jié)構(gòu)存儲(chǔ),最簡(jiǎn)單的就直接用Map來(lái)存儲(chǔ)數(shù)據(jù);或者復(fù)雜的如redis一樣提供了多種數(shù)據(jù)類型哈希,列表,集合,有序集合等,底層使用了雙端鏈表,壓縮列表,集合,跳躍表等數(shù)據(jù)結(jié)構(gòu);

2.對(duì)象上限

因?yàn)槭潜镜鼐彺?,?nèi)存有上限,所以一般都會(huì)指定緩存對(duì)象的數(shù)量比如1024,當(dāng)達(dá)到某個(gè)上限后需要有某種策略去刪除多余的數(shù)據(jù);

3.清除策略

上面說(shuō)到當(dāng)達(dá)到對(duì)象上限之后需要有清除策略,常見(jiàn)的比如有LRU(最近最少使用)、FIFO(先進(jìn)先出)、LFU(最近最不常用)、SOFT(軟引用)、WEAK(弱引用)等策略;

4.過(guò)期時(shí)間

除了使用清除策略,一般本地緩存也會(huì)有一個(gè)過(guò)期時(shí)間設(shè)置,比如redis可以給每個(gè)key設(shè)置一個(gè)過(guò)期時(shí)間,這樣當(dāng)達(dá)到過(guò)期時(shí)間之后直接刪除,采用清除策略+過(guò)期時(shí)間雙重保證;

5.線程安全

像redis是直接使用單線程處理,所以就不存在線程安全問(wèn)題;而我們現(xiàn)在提供的本地緩存往往是可以多個(gè)線程同時(shí)訪問(wèn)的,所以線程安全是不容忽視的問(wèn)題;并且線程安全問(wèn)題是不應(yīng)該拋給使用者去保證;

6.簡(jiǎn)明的接口

提供一個(gè)傻瓜式的對(duì)外接口是很有必要的,對(duì)使用者來(lái)說(shuō)使用此緩存不是一種負(fù)擔(dān)而是一種享受;提供常用的get,put,remove,clear,getSize方法即可;

7.是否持久化

這個(gè)其實(shí)不是必須的,是否需要將緩存數(shù)據(jù)持久化看需求;本地緩存如ehcache是支持持久化的,而guava是沒(méi)有持久化功能的;分布式緩存如redis是有持久化功能的,memcached是沒(méi)有持久化功能的;

8.阻塞機(jī)制

在看Mybatis源碼的時(shí)候,二級(jí)緩存提供了一個(gè)blocking標(biāo)識(shí),表示當(dāng)在緩存中找不到元素時(shí),它設(shè)置對(duì)緩存鍵的鎖定;這樣其他線程將等待此元素被填充,而不是命中數(shù)據(jù)庫(kù);其實(shí)我們使用緩存的目的就是因?yàn)楸痪彺娴臄?shù)據(jù)生成比較費(fèi)時(shí),比如調(diào)用對(duì)外的接口,查詢數(shù)據(jù)庫(kù),計(jì)算量很大的結(jié)果等等;這時(shí)候如果多個(gè)線程同時(shí)調(diào)用get方法獲取的結(jié)果都為null,每個(gè)線程都去執(zhí)行一遍費(fèi)時(shí)的計(jì)算,其實(shí)也是對(duì)資源的浪費(fèi);最好的辦法是只有一個(gè)線程去執(zhí)行,其他線程等待,計(jì)算一次就夠了;但是此功能基本上都交給使用者來(lái)處理,很少有本地緩存有這種功能;

如何實(shí)現(xiàn)

以上大致介紹了實(shí)現(xiàn)一個(gè)本地緩存我們都有哪些需要考慮的地方,當(dāng)然可能還有其他沒(méi)有考慮到的點(diǎn);下面繼續(xù)看看關(guān)于每個(gè)點(diǎn)都應(yīng)該如何去實(shí)現(xiàn),重點(diǎn)介紹一下思路;

1.數(shù)據(jù)結(jié)構(gòu)

本地緩存最常見(jiàn)的是直接使用Map來(lái)存儲(chǔ),比如guava使用ConcurrentHashMap,ehcache也是用了ConcurrentHashMap,Mybatis二級(jí)緩存使用HashMap來(lái)存儲(chǔ):

????Map<Object,?Object>?cache?=?new?ConcurrentHashMap<Object,?Object>()Mybatis使用HashMap本身是非線程安全的,所以可以看到起內(nèi)部使用了一個(gè)SynchronizedCache用來(lái)包裝,保證線程的安全性; ?

當(dāng)然除了使用Map來(lái)存儲(chǔ),可能還使用其他數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ),比如redis使用了雙端鏈表,壓縮列表,整數(shù)集合,跳躍表和字典;當(dāng)然這主要是因?yàn)閞edis對(duì)外提供的接口很豐富除了哈希還有列表,集合,有序集合等功能;

2.對(duì)象上限

本地緩存常見(jiàn)的一個(gè)屬性,一般緩存都會(huì)有一個(gè)默認(rèn)值比如1024,在用戶沒(méi)有指定的情況下默認(rèn)指定;當(dāng)緩存的數(shù)據(jù)達(dá)到指定最大值時(shí),需要有相關(guān)策略從緩存中清除多余的數(shù)據(jù)這就涉及到下面要介紹的清除策略;

3.清除策略

配合對(duì)象上限之后使用,場(chǎng)景的清除策略如:LRU(最近最少使用)、FIFO(先進(jìn)先出)、LFU(最近最不常用)、SOFT(軟引用)、WEAK(弱引用);?
LRU :Least Recently
Used的縮寫最近最少使用,移除最長(zhǎng)時(shí)間不被使用的對(duì)象;常見(jiàn)的使用LinkedHashMap來(lái)實(shí)現(xiàn),也是很多本地緩存默認(rèn)使用的策略;?
FIFO :先進(jìn)先出,按對(duì)象進(jìn)入緩存的順序來(lái)移除它們;常見(jiàn)使用隊(duì)列Queue來(lái)實(shí)現(xiàn);?
LFU :Least Frequently
Used的縮寫大概也是最近最少使用的意思,和LRU有點(diǎn)像;區(qū)別點(diǎn)在LRU的淘汰規(guī)則是基于訪問(wèn)時(shí)間,而LFU是基于訪問(wèn)次數(shù)的;可以通過(guò)HashMap并且記錄訪問(wèn)次數(shù)來(lái)實(shí)現(xiàn);?
SOFT :軟引用基于垃圾回收器狀態(tài)和軟引用規(guī)則移除對(duì)象;常見(jiàn)使用SoftReference來(lái)實(shí)現(xiàn);?
WEAK :弱引用更積極地基于垃圾收集器狀態(tài)和弱引用規(guī)則移除對(duì)象;常見(jiàn)使用WeakReference來(lái)實(shí)現(xiàn);

4.過(guò)期時(shí)間

設(shè)置過(guò)期時(shí)間,讓緩存數(shù)據(jù)在指定時(shí)間過(guò)后自動(dòng)刪除;常見(jiàn)的過(guò)期數(shù)據(jù)刪除策略有兩種方式:被動(dòng)刪除和主動(dòng)刪除;?
被動(dòng)刪除 :每次進(jìn)行g(shù)et/put操作的時(shí)候都會(huì)檢查一下當(dāng)前key是否已經(jīng)過(guò)期,如果過(guò)期則刪除,類似如下代碼:

????if?(System.currentTimeMillis()?-?lastClear?>?clearInterval)?{
??????????clear();
????}
????

主動(dòng)刪除 :專門有一個(gè)job在后臺(tái)定期去檢查數(shù)據(jù)是否過(guò)期,如果過(guò)期則刪除,這其實(shí)可以有效的處理冷數(shù)據(jù);

5.線程安全

盡量用線程安全的類去存儲(chǔ)數(shù)據(jù),比如使用ConcurrentHashMap代替HashMap;或者提供相應(yīng)的同步處理類,比如Mybatis提供了SynchronizedCache:

?????public?synchronized?void?putObject(Object?key,?Object?object)?{
????????...省略...
??????}

??????@Override
??????public?synchronized?Object?getObject(Object?key)?{
????????...省略...
??????}
????

6.簡(jiǎn)明的接口

提供常用的get,put,remove,clear,getSize方法即可,比如Mybatis的Cache接口:

????public?interface?Cache?{
??????String?getId();
??????void?putObject(Object?key,?Object?value);
??????Object?getObject(Object?key);
??????Object?removeObject(Object?key);
??????void?clear();
??????int?getSize();
??????ReadWriteLock?getReadWriteLock();
????}
????

再來(lái)看看guava提供的Cache接口,相對(duì)來(lái)說(shuō)也是比較簡(jiǎn)潔的:

????public?interface?Cache<K,?V>?{
??????V?getIfPresent(@CompatibleWith("K")?Object?key);
??????V?get(K?key,?Callable?loader)?throws?ExecutionException;
??????ImmutableMap?getAllPresent(Iterable?keys);
??????void?put(K?key,?V?value);
??????void?putAll(Map?m);
??????void?invalidate(@CompatibleWith("K")?Object?key);
??????void?invalidateAll(Iterable?keys);
??????void?invalidateAll();
??????long?size();
??????CacheStats?stats();
??????ConcurrentMap?asMap();
??????void?cleanUp();
????}

7.是否持久化

持久化的好處是重啟之后可以再次加載文件中的數(shù)據(jù),這樣就起到類似熱加載的功效;比如ehcache提供了是否持久化磁盤緩存的功能,將緩存數(shù)據(jù)存放在一個(gè).data文件中;

????diskPersistent="false"?//是否持久化磁盤緩存

redis更是將持久化功能發(fā)揮到極致,慢慢的有點(diǎn)像數(shù)據(jù)庫(kù)了;提供了AOF和RDB兩種持久化方式;當(dāng)然很多情況下可以配合使用兩種方式;

8.阻塞機(jī)制

除了在Mybatis中看到了BlockingCache來(lái)實(shí)現(xiàn)此功能,之前在看< > 的時(shí)候其中有實(shí)現(xiàn)一個(gè)很完美的緩存,大致代碼如下:

????public?class?Memoizerl<A,?V>?implements?Computable<A,?V>?{
????????private?final?Map>?cache?=?new?ConcurrentHashMap>();
????????private?final?Computable?c;

????????public?Memoizerl(Computable?c)?{
????????????this.c?=?c;
????????}

????????@Override
????????public?V?compute(A?arg)?throws?InterruptedException,?ExecutionException?{
????????????while?(true)?{
????????????????Future?f?=?cache.get(arg);
????????????????if?(f?==?null)?{
????????????????????Callable?eval?=?new?Callable()?{
????????????????????????@Override
????????????????????????public?V?call()?throws?Exception?{
????????????????????????????return?c.compute(arg);
????????????????????????}
????????????????????};
????????????????????FutureTask?ft?=?new?FutureTask(eval);
????????????????????f?=?cache.putIfAbsent(arg,?ft);
????????????????????if?(f?==?null)?{
????????????????????????f?=?ft;
????????????????????????ft.run();
????????????????????}
????????????????????try?{
????????????????????????return?f.get();
????????????????????}?catch?(CancellationException?e)?{
????????????????????????cache.remove(arg,?f);
????????????????????}
????????????????}
????????????}
????????}
????}

compute是一個(gè)計(jì)算很費(fèi)時(shí)的方法,所以這里把計(jì)算的結(jié)果緩存起來(lái),但是有個(gè)問(wèn)題就是如果兩個(gè)線程同時(shí)進(jìn)入此方法中怎么保證只計(jì)算一次,這里最核心的地方在于使用了ConcurrentHashMap的putIfAbsent方法,同時(shí)只會(huì)寫入一個(gè)FutureTask;

總結(jié)

本文大致介紹了要設(shè)計(jì)一個(gè)本地緩存都需要考慮哪些點(diǎn):數(shù)據(jù)結(jié)構(gòu),對(duì)象上限,清除策略,過(guò)期時(shí)間,線程安全,阻塞機(jī)制,實(shí)用的接口,是否持久化;當(dāng)然肯定有其他考慮點(diǎn),歡迎補(bǔ)充。

特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:

如何設(shè)計(jì)一個(gè)強(qiáng)悍的本地緩存?

如何設(shè)計(jì)一個(gè)強(qiáng)悍的本地緩存?

如何設(shè)計(jì)一個(gè)強(qiáng)悍的本地緩存?

長(zhǎng)按訂閱更多精彩▼

如何設(shè)計(jì)一個(gè)強(qiáng)悍的本地緩存?

如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動(dòng)電源

在工業(yè)自動(dòng)化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動(dòng)力設(shè)備,其驅(qū)動(dòng)電源的性能直接關(guān)系到整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動(dòng)勢(shì)抑制與過(guò)流保護(hù)是驅(qū)動(dòng)電源設(shè)計(jì)中至關(guān)重要的兩個(gè)環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動(dòng)性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動(dòng)電源

LED 驅(qū)動(dòng)電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個(gè)照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動(dòng)電源易損壞的問(wèn)題卻十分常見(jiàn),不僅增加了維護(hù)成本,還影響了用戶體驗(yàn)。要解決這一問(wèn)題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動(dòng)電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動(dòng)電源的公式,電感內(nèi)電流波動(dòng)大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動(dòng)電源

電動(dòng)汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動(dòng)汽車的核心技術(shù)之一是電機(jī)驅(qū)動(dòng)控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動(dòng)系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動(dòng)汽車的動(dòng)力性能和...

關(guān)鍵字: 電動(dòng)汽車 新能源 驅(qū)動(dòng)電源

在現(xiàn)代城市建設(shè)中,街道及停車場(chǎng)照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢(shì)逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動(dòng)電源 LED

LED通用照明設(shè)計(jì)工程師會(huì)遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動(dòng)電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動(dòng)電源的電磁干擾(EMI)問(wèn)題成為了一個(gè)不可忽視的挑戰(zhàn)。電磁干擾不僅會(huì)影響LED燈具的正常工作,還可能對(duì)周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來(lái)解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動(dòng)電源

開(kāi)關(guān)電源具有效率高的特性,而且開(kāi)關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動(dòng)電源

關(guān)鍵字: LED 驅(qū)動(dòng)電源 開(kāi)關(guān)電源

LED驅(qū)動(dòng)電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動(dòng)LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動(dòng)電源
關(guān)閉