java分布式緩存方案
一、從數(shù)據(jù)說(shuō)起
我們?cè)僮鼍彺嬷靶枰褦?shù)據(jù)先分好類
按變化頻率:
- 靜態(tài)數(shù)據(jù):一般不變的,類似于字典表
- 準(zhǔn)靜態(tài)數(shù)據(jù):變化頻率很低,部門結(jié)構(gòu)設(shè)置,全國(guó)行政區(qū)劃數(shù)據(jù)
- 中間狀態(tài)數(shù)據(jù):一些計(jì)算的可復(fù)用中間數(shù)據(jù),變量副本,配置中心的本地副本
按使用頻率:
- 熱數(shù)據(jù):使用頻率高的
- 讀寫比大的:讀的頻率遠(yuǎn)大于寫的頻率
這些數(shù)據(jù)就比較適合使用緩存。
緩存無(wú)處不在。內(nèi)存可以看作是cpu和磁盤之間的緩存。cpu與內(nèi)存的處理速度也不一致,所以出現(xiàn)了L1&L2 Cache
緩存的本質(zhì):系統(tǒng)各級(jí)之間處理速度不匹配,利用空間換時(shí)間。
緩存加載時(shí)間
1. 啟動(dòng)時(shí)全量加載
2. 懶加載
2.1. 同步使用加載
先看緩存里是否有數(shù)據(jù),沒(méi)有的話從數(shù)據(jù)庫(kù)讀取。讀取的數(shù)據(jù),先放到內(nèi)存,然后返回給調(diào)用方。
2.2. 延遲異步加載
從緩存里獲取數(shù)據(jù),不管有沒(méi)有都直接返回。
策略1:如果緩存為空的話,則發(fā)起一個(gè)異步線程負(fù)責(zé)加載。
策略2:異步線程負(fù)責(zé)維護(hù)緩存的數(shù)據(jù),定期或根據(jù)條件觸發(fā)更新。
緩存過(guò)期策略
- 按FIFO或LRU
- 固定時(shí)間過(guò)期
- 根據(jù)業(yè)務(wù)進(jìn)行時(shí)間的加權(quán)。
二、本地緩存
1.Map 緩存
public static final Map<String,Object> CACHE=new HashMap();
CACHE.put("key","value");
2.Guava緩存
Cache<String,String> cache = CacheBuilder.newBuilder() .maximumSize(1024) .expireAfterWrite(60,TimeUnit.SECONDS) .weakValues() .build();
cache.put("word","Hello Guava Cache");
System.out.println(cache.getIfPresent("word"));
3.Spring Cache
- 基于注解和AOP,使用方便
- 可以配置Condition和SPEL,非常靈活
- 需要注意:繞過(guò)Spring的話,注解無(wú)效
核心功能:@Cacheable、@CachePut、@CacheEvict
本地緩存的缺點(diǎn):
- 在集群環(huán)境中,如果每個(gè)節(jié)點(diǎn)都保存一份緩存,導(dǎo)致占用內(nèi)存變大
- 在JVM中長(zhǎng)期存在,會(huì)影響GC
- 緩存數(shù)據(jù)的調(diào)度處理,影響業(yè)務(wù)線程,爭(zhēng)奪資源
三、遠(yuǎn)程緩存
1.Redis
Redis是一個(gè)開源的使用ANSI C語(yǔ)言編寫的,基于內(nèi)存也可以持久化的key-value數(shù)據(jù)庫(kù),并提供多種語(yǔ)言的API
2. Memcached
memcached是一套分布式的高速緩存系統(tǒng),由LiveJournal的Brad Fitzpatrick開發(fā),但被許多網(wǎng)站使用。這是一套開放源代碼軟件,以BSD license授權(quán)發(fā)布。
四、內(nèi)存網(wǎng)格
- Hazelcast
- lgnite
五、緩存常見(jiàn)問(wèn)題
1. 緩存穿透
問(wèn)題描述:大量并發(fā)查詢不存在的KEY,導(dǎo)致都直接把壓力透?jìng)鞯綌?shù)據(jù)庫(kù)上。
分析:因?yàn)閿?shù)據(jù)庫(kù)里沒(méi)有值,所以沒(méi)有建立緩存,導(dǎo)致一直打到數(shù)據(jù)庫(kù)上。
解決辦法:
- 緩存空值的KEY
- Bloom過(guò)濾或RoaringBitmap判斷KEY是否存在
- 完全以緩存為準(zhǔn),使用延遲異步加載的方式去加載數(shù)據(jù)庫(kù)數(shù)據(jù)到緩存。
Bloom過(guò)濾器示例:
(引入guava依賴)
public static void main(String[] args) {
BloomFilter<CharSequence> filter = BloomFilter.create(
Funnels.stringFunnel(Charsets.UTF_8),//Funnels.integerFunnel(), //數(shù)據(jù)格式
1000000,//預(yù)計(jì)存入數(shù)據(jù)量
0.01);//誤判率
System.out.println(filter.mightContain("abcdefg"));
filter.put("abcdefg");
System.out.println(filter.mightContain("abcdefg"));
}
RoaringBitmap示例:
引入依賴:
<dependency> <groupId>org.roaringbitmap</groupId> <artifactId>RoaringBitmap</artifactId> <version>0.8.1</version> </dependency>
public static void test3(){
Roaring64NavigableMap roaring64NavigableMap = Roaring64NavigableMap.bitmapOf(3, 4, 5, 90);
//是否包含
boolean contains = roaring64NavigableMap.contains(3);
long l = roaring64NavigableMap.rankLong(3);
System.out.println(l);
System.out.println(contains);
}
2. 緩存擊穿
問(wèn)題:當(dāng)某個(gè)KEY失效的時(shí)候,正好有大量并發(fā)請(qǐng)求訪問(wèn)這個(gè)KEY
分析:跟緩存穿透比較像,這個(gè)是屬于偶然的
解決辦法:
- KEY的更新的時(shí)候添加全局互斥鎖
- 完全以緩存為準(zhǔn),使用延遲異步加載的策略
3. 緩存雪崩
問(wèn)題:當(dāng)某一個(gè)時(shí)刻發(fā)生大規(guī)模的緩存失效的情況,會(huì)有大量請(qǐng)求打到數(shù)據(jù)庫(kù),導(dǎo)致數(shù)據(jù)庫(kù)壓力過(guò)大而宕機(jī)
分析:一般來(lái)說(shuō),由于更新策略、或者數(shù)據(jù)熱點(diǎn)、緩存服務(wù)宕機(jī)等原因,導(dǎo)致緩存數(shù)據(jù)同時(shí)大規(guī)模不可以。
解決辦法:
- 緩存更新、失效策略在時(shí)間上做到比較均勻
- 使用的熱數(shù)據(jù)盡量分散到不同機(jī)器上
- 多臺(tái)機(jī)器做主從復(fù)制,實(shí)現(xiàn)高可用
- 實(shí)現(xiàn)熔斷限流機(jī)制,對(duì)系統(tǒng)進(jìn)行負(fù)載能力控制
- 使用本地緩存兜底
番外:
布隆過(guò)濾器:
目標(biāo)就是要基于過(guò)濾器已存儲(chǔ)生成的原始元數(shù)據(jù),進(jìn)行比較過(guò)濾,如果是在原始元數(shù)據(jù)集合里面的,一定會(huì)被發(fā)現(xiàn)。也有可能不是里面的被誤殺。
BloomFilter 會(huì)開辟一個(gè)m位的bitArray(位數(shù)組),開始所有數(shù)據(jù)都部署為0,當(dāng)一個(gè)元素過(guò)來(lái)的時(shí)候,通過(guò)多個(gè)hash函數(shù)計(jì)算出不同的值,然后根據(jù)hash值找到對(duì)應(yīng)的下標(biāo)處,將里面的值改為1.

優(yōu)點(diǎn):使用計(jì)算,節(jié)省存儲(chǔ)空間。
缺點(diǎn):有失誤率。不是在過(guò)濾器原始表里的數(shù)據(jù)也會(huì)被誤算進(jìn)去。
使用場(chǎng)景:目標(biāo)就是要基于過(guò)濾器已存儲(chǔ)生成的原始元數(shù)據(jù),進(jìn)行比較過(guò)濾,如果是在原始元數(shù)據(jù)集合里面的,一定會(huì)被發(fā)現(xiàn)。布隆過(guò)濾器核心正確的使用就是進(jìn)行過(guò)濾禁止,進(jìn)行正確的否定。
舉例:如我們有100萬(wàn)個(gè)黑名單的url地址,過(guò)來(lái)一個(gè)地址我們算出來(lái)不在里面,那就肯定可以放行。
BitMap:
BitMap的基本思想是用一個(gè)bit位來(lái)標(biāo)記某個(gè)元素對(duì)應(yīng)的值,這樣就可以大大節(jié)省空間。
在Java中一個(gè)int占4個(gè)字節(jié),也就是32bit。按int存儲(chǔ)和按位存儲(chǔ)的大小差距是32倍。
那么怎么表示一個(gè)數(shù)呢?可以使用1表示存在,0表示不存在。
如下面:表示{2,6}

一個(gè)byte只有8個(gè)位置,如果想表示13怎么辦呢?只能再用一個(gè)byte了,就成了一個(gè)二維數(shù)組了

1個(gè)int占32位,那么我們只需要申請(qǐng)一個(gè)int數(shù)組長(zhǎng)度為 int tmp[1+N/32] 即可存儲(chǔ),其中N表示要存儲(chǔ)的這些數(shù)中的最大值
使用場(chǎng)景:
- 1.快速排序
把數(shù)放進(jìn)去之后,遍歷一遍,把值是1的都取出來(lái)就排好序了。
- 2.快速去重
20億個(gè)整數(shù)中找出不重復(fù)的整數(shù)的個(gè)數(shù)?
內(nèi)存不足以容納這20億個(gè)整數(shù)。我們?cè)趺幢硎緮?shù)字的狀態(tài)呢?一個(gè)數(shù)的狀態(tài)可以分為3種,不存在、存在一次、存在兩次及以上。這就需要兩個(gè)bit來(lái)表示。00代表不存在,01代表一次,11代表兩次及以上。
接下來(lái)我們就把這20億個(gè)整數(shù)放進(jìn)去,如果狀態(tài)為00,就改為01,如果狀態(tài)為01就改為11.如果狀態(tài)為11,就不動(dòng)了。都放完后,遍歷取出值為01的,就是不重復(fù)的數(shù)據(jù)的個(gè)數(shù)。。
- 3. 快速查找
給定一個(gè)整數(shù)M,M/32就能得到int數(shù)組的下標(biāo),M%32就知道在這個(gè)下標(biāo)里面的具體位置。
如13,就能算出在int[0]里面的第13個(gè)
到此這篇關(guān)于分布式緩存的文章就介紹到這了,更多相關(guān)緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring注解@Configuration與@Bean注冊(cè)組件的使用詳解
這篇文章主要介紹了SpringBoot中的注解@Configuration與@Bean注冊(cè)組件的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2022-06-06
IntelliJ IDEA中ajax開發(fā)實(shí)現(xiàn)分頁(yè)查詢示例
這篇文章主要介紹了IntelliJ IDEA中ajax開發(fā)實(shí)現(xiàn)分頁(yè)查詢,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
Spring Cloud Gateway層限流實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了Spring Cloud Gateway層限流實(shí)現(xiàn)過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
SpringBoot+Redis實(shí)現(xiàn)查找附近用戶的示例代碼
SpringDataRedis提供了十分簡(jiǎn)單的地理位置定位的功能,本文主要介紹了SpringBoot+Redis實(shí)現(xiàn)查找附近用戶的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02
FilenameUtils.getName?函數(shù)源碼分析
這篇文章主要為大家介紹了FilenameUtils.getName?函數(shù)源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Java基礎(chǔ)之查找文本特定內(nèi)容后進(jìn)行修改
這篇文章主要介紹了Java基礎(chǔ)之查找文本特定內(nèi)容后進(jìn)行修改,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04
JavaFX實(shí)現(xiàn)簡(jiǎn)單日歷效果
這篇文章主要為大家詳細(xì)介紹了JavaFX實(shí)現(xiàn)簡(jiǎn)單日歷效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11

