神奇的閃電緩存,帶系統(tǒng)飛
本文轉(zhuǎn)載自微信公眾號(hào)「小姐姐味道」,作者小姐姐味道 。轉(zhuǎn)載本文請聯(lián)系小姐姐味道公眾號(hào)。
緩存,在高并發(fā)的應(yīng)用中,用的那是相當(dāng)多。為什么?就因?yàn)镮/O實(shí)在是慢!為了解決不同組件之間的速度差,大家都寄希望于加入一個(gè)中間層,期待產(chǎn)生一些魔幻的事。
就拿Redis來說,火的就一塌糊涂,但中間會(huì)產(chǎn)生很多數(shù)據(jù)同步和數(shù)據(jù)一致性問題。有的牛x公司嫌煩,同時(shí)有錢,干脆干掉緩存后面的DB,直接把所有的數(shù)據(jù)放在了緩存上。哦不,這時(shí)候緩存已經(jīng)不叫做緩存,應(yīng)該叫做快存,因?yàn)樗罱K是要通過rdb落地的。
看到這里,先不要懷疑事實(shí)的正確性。有些公司的業(yè)務(wù),確實(shí)不需要什么關(guān)系型數(shù)據(jù)庫,一個(gè)redis就能玩得轉(zhuǎn)。
閃電緩存場景
那閃電緩存又是何方神圣?實(shí)在不好意思, 這個(gè)名詞,是xjjdog自創(chuàng)的。
它用在下面的場景之中。
- 一份數(shù)據(jù),通過耗時(shí)的請求獲取之后,會(huì)在極短的時(shí)間內(nèi),再次被用到。
- 業(yè)務(wù)對數(shù)據(jù)的一致性要求不是特別強(qiáng)烈,但也不是無底線忍受。
- 內(nèi)存的空間有限,不適合把大量數(shù)據(jù)放在內(nèi)存中。
- 數(shù)據(jù)的使用跨方法、跨代碼塊、甚至跨線程,只在時(shí)間概念上有關(guān)聯(lián)
這個(gè)時(shí)候,我們就可以將數(shù)據(jù)緩存一小段時(shí)間,盡量在下次的使用的時(shí)候,從這個(gè)時(shí)間極短的緩存中獲取。
srping-data-jpa背后的Hibernate一級(jí)緩存,在同一session下的數(shù)據(jù)被自動(dòng)緩存,可以變相的看作是閃電緩存的一種實(shí)現(xiàn)。不過人家叫一級(jí)緩存,顯得更高大上一些,應(yīng)用也更局限一些。
Java有多種緩存數(shù)據(jù)的方法,也有不同的生命周期。你可以想一下Session中的數(shù)據(jù)該如何存取,也可以想一下Java框架中各種各樣的Context,都是為了共享數(shù)據(jù)。
實(shí)現(xiàn)方式
閃電緩存,在Java中其實(shí)是有多種方式的,也有各種各樣的優(yōu)缺點(diǎn)。
ThreadLocal
第一種方式,就是ThreadLocal。拿最常用的Spring來說,它事務(wù)管理的傳播機(jī)制,就是使用ThreadLocal實(shí)現(xiàn)的。
我可以在數(shù)據(jù)第一次被獲取的時(shí)候,使用set方法給它設(shè)置一個(gè)值。然后在最后一個(gè)操作用的使用,把它remove掉,變相的實(shí)現(xiàn)請求級(jí)別的閃電緩存。(為什么要remove?因?yàn)樵诰€程池中可能會(huì)有復(fù)用的問題)
但由于ThreadLocal是線程私有的,所以它不能夠跨線程。上面Spring的事務(wù)傳播機(jī)制是不能夠跨線程的,我們的閃電緩存也是不能夠跨線程的。
這就決定了ThreadLocal的應(yīng)用場景有限。但它還有其他兩個(gè)硬傷:
ThreadLocal的生命周期不好管理,何時(shí)生成,何時(shí)銷毀,都是問題
ThreadLocal使用自定義的ThreadLocalMap。它雖然叫Map,但卻沒有實(shí)現(xiàn)Map的接口,它使用開放尋址(遇到?jīng)_突,依次查找,直到空閑位置)的方法,這種方式是非常低效的
綜上三點(diǎn),ThreadLocal這個(gè)方案其實(shí)并不太好。
普通Cache加過期時(shí)間
我們可以變換一下思路,使用普通的Cache,然后給它一個(gè)超短的緩存時(shí)間,那么就可以變相的實(shí)現(xiàn)閃電緩存的功能。
實(shí)現(xiàn)也是非常簡單的。比如,下面幾行代碼,就是一個(gè)對對象緩存了3秒的例子。
- LoadingCache<String, String> lc = CacheBuilder
- .newBuilder()
- .expireAfterWrite(3,TimeUnit.SECONDS)
- .build(new CacheLoader<String, String>() {
- @Override
- public String load(String key) throws Exception {
- return slowMethod(key);
- }});
在這3秒之間,系統(tǒng)中所有用到這個(gè)數(shù)據(jù)的請求,都可以達(dá)到復(fù)用的效果。這對于并發(fā)量非常高的應(yīng)用來說,減少的請求量可能是數(shù)量級(jí)的。
我曾經(jīng)就做過一個(gè)對用戶基本信息的優(yōu)化,把對用戶服務(wù)的請求量從8w/s,降低到1000/s,一度讓負(fù)責(zé)服務(wù)的同學(xué)以為上游業(yè)務(wù)當(dāng)機(jī)了。
End
技術(shù)通常都是工具,只有真正用到業(yè)務(wù)場景中,才有它的價(jià)值。閃電緩存這個(gè)概念本身沒有什么神奇的,它的最優(yōu)實(shí)現(xiàn)方式,竟然是普通的Cache加極短的過期時(shí)間。
技術(shù)本身是件非常簡單的事情,但想到它應(yīng)用的場景,卻是比較難的。事實(shí)上,我已經(jīng)把這個(gè)概念做到了我的ppt上,展現(xiàn)的時(shí)候大家比較迷惑,以為是什么高大上的東西,我自熱也不好意思戳破這層窗戶紙,就讓它繼續(xù)神秘下去吧。
如今xjjdog告訴你了,要保密哦。
作者簡介:小姐姐味道 (xjjdog),一個(gè)不允許程序員走彎路的公眾號(hào)。聚焦基礎(chǔ)架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。我的個(gè)人微信xjjdog0,歡迎添加好友,進(jìn)一步交流。