亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Redis高并發(fā)緩存問題分析及解決過程

 更新時(shí)間:2025年01月10日 09:48:06   作者:一條行走的魚  
文章總結(jié)了Redis緩存的六種常見問題及其解決方案:緩存穿透、緩存擊穿、緩存雪崩、熱點(diǎn)key重建優(yōu)化、緩存和數(shù)據(jù)庫雙寫不一致,以及Redis對過期key的三種清除策略,每種問題都提供了詳細(xì)的原因分析和具體的解決方案

Redis緩存問題解決方案

1.緩存穿透

1)什么是緩存穿透

緩存穿透是指查詢一個(gè)根本不存在的數(shù)據(jù),緩存層和存儲(chǔ)層都不會(huì)命中,通常出于容錯(cuò)的考慮,如果從存儲(chǔ)層查不到數(shù)據(jù)則不寫入緩存層。

緩存穿透將導(dǎo)致不存在的數(shù)據(jù)每次請求都要到存儲(chǔ)層去查詢, 失去了緩存保護(hù)后端存儲(chǔ)的意義。

2)造成緩存穿透的原因

1.自身業(yè)務(wù)出現(xiàn)問題或者數(shù)據(jù)有問題。

2.黑客攻擊,制造大量不存在的key 利用壓測工具等進(jìn)行攻擊

3)解決方案

1.緩存空對象

String get(String key){
    //先從緩存中拿數(shù)據(jù)
    String cacheValue = cache.get(key);
    // 緩存為空
    if (StringUtils.isBlank(cacheValue)) {
        //從db中拿
        String storageValue = storage.get(key);
        cache.set(key, storageValue);
        //設(shè)置一個(gè)過期時(shí)間,否則緩存中有大量的空對象
        if (storageValue == null) { 
            cache.expire(key, 60 * 5); 12
        }
        return storageValue;
        
    }else{
        //緩存中可以獲取之間返回
        return cacheValue;
    }
}

2.布隆過濾器

對于惡意攻擊,向服務(wù)器請求大量不存在的數(shù)據(jù)造成的緩存穿透,還可以用布隆過濾器先做一次過濾,對于不 存在的數(shù)據(jù)布隆過濾器一般都能夠過濾掉,不讓請求再往后端發(fā)送。

當(dāng)布隆過濾器說某個(gè)值存在時(shí),這個(gè)值可 能不存在;當(dāng)它說不存在時(shí),那就肯定不存在

布隆過濾器就是一個(gè)大型的位數(shù)組和幾個(gè)不一樣的無偏 hash 函數(shù)。所謂無偏就是能夠把元素的 hash 值算得比較均勻。

向布隆過濾器中添加 key 時(shí),會(huì)使用多個(gè) hash 函數(shù)對 key 進(jìn)行 hash 算得一個(gè)整數(shù)索引值然后對位數(shù)組長度進(jìn)行取模運(yùn)算得到一個(gè)位置,每個(gè) hash 函數(shù)都會(huì)算得一個(gè)不同的位置。再把位數(shù)組的這幾個(gè)位置都置為1就完成了 add 操作。

向布隆過濾器詢問 key 是否存在時(shí),跟 add 一樣,也會(huì)把 hash 的幾個(gè)位置都算出來,看看位數(shù)組中這幾個(gè)位置是否都為 1,只要有一個(gè)位為 0,那么說明布隆過濾器中這個(gè)key 不存在。如果都是 1,這并不能說明這個(gè) key 就一定存在,只是極有可能存在,因?yàn)檫@些位被置為1可能是因?yàn)槠渌?key 存在所致。如果這個(gè)位數(shù)組比較稀疏,這個(gè)概率就會(huì)很大,如果這個(gè)位數(shù)組比較擁擠,這個(gè)概率就會(huì)降低。 這種方法適用于數(shù)據(jù)命中不高、 數(shù)據(jù)相對固定、 實(shí)時(shí)性低(通常是數(shù)據(jù)集較大) 的應(yīng)用場景, 代碼維護(hù)較為 復(fù)雜, 但是緩存空間占用很少。

可以用redisson實(shí)現(xiàn)布隆過濾器,引入依賴:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.6.5</version>
</dependency>    

實(shí)例代碼

package com.redisson;
import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class RedissonBloomFilter{
    
    public static void main(String[] args){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        //構(gòu)造Redisson
        RedissonClient redisson = Redisson.create(config);
        RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");
        //初始化布隆過濾器:預(yù)計(jì)元素為100000000L,誤差率為3%,根據(jù)這兩個(gè)參數(shù)會(huì)計(jì)算出底層的bit數(shù)組大小
        bloomFilter.tryInit(100000000L,0.03);
        //將zhuge插入到布隆過濾器中
        bloomFilter.add("zhuge");
        //判斷下面號(hào)碼是否在布隆過濾器中
        System.out.println(bloomFilter.contains("123"));//false
        System.out.println(bloomFilter.contains("baiqi"));//false
        System.out.println(bloomFilter.contains("qianyue"));//true
    }
    
}

使用布隆過濾器需要把所有數(shù)據(jù)提前放入布隆過濾器,并且在增加數(shù)據(jù)時(shí)也要往布隆過濾器里放,布隆過濾器 緩存過濾偽代碼:

//初始化布隆過濾器
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");
//初始化布隆過濾器:預(yù)計(jì)元素為100000000L,誤差率為3%
bloomFilter.tryInit(100000000L,0.03);
//把所有數(shù)據(jù)存入布隆過濾器
void init(){
    for (String key: keys) {
        bloomFilter.put(key);
    }
}
String get(String key){
    // 從布隆過濾器這一級(jí)緩存判斷下key是否存在
    Boolean exist = bloomFilter.contains(key);
    if(!exist){
        return "";
    }
    // 從緩存中獲取數(shù)據(jù)
    String cacheValue = cache.get(key);
    // 緩存為空
    if (StringUtils.isBlank(cacheValue)){
        // 從db中獲取
        String storageValue = storage.get(key);
        cache.set(key, storageValue);
        // 如果存儲(chǔ)數(shù)據(jù)為空, 需要設(shè)置一個(gè)過期時(shí)間(300秒)
        if (storageValue == null){
            cache.expire(key, 60 * 5);
        }
        return storageValue;
    }else{
        return cacheValue;
    }
    
}

注意:布隆過濾器不能刪除數(shù)據(jù),如果要?jiǎng)h除得重新初始化數(shù)據(jù)。

2.緩存擊穿

1)什么是緩存擊穿

由于大批量緩存在同一時(shí)間失效可能導(dǎo)致大量請求同時(shí)穿透緩存直達(dá)數(shù)據(jù)庫,可能會(huì)造成數(shù)據(jù)庫瞬間壓力過大甚至掛掉。

2)解決方案

在批量增加緩存時(shí)最好將這一批數(shù)據(jù)的緩存過期時(shí)間設(shè)置為一個(gè)時(shí)間段內(nèi)的不同時(shí)間。

2.緩存雪崩

1)什么是緩存雪崩

緩存雪崩指的是緩存層支撐不住或宕掉后,流量會(huì)像奔逃的野牛一樣,打向后端存儲(chǔ)層。 由于緩存層承載著大量請求, 有效地保護(hù)了存儲(chǔ)層,但是如果緩存層由于某些原因不能提供服務(wù)(比如超大并發(fā)過來,緩存層支撐不住,或者由于緩存設(shè)計(jì)不好,類似大量請求訪問bigkey,導(dǎo)致緩存能支撐的并發(fā)急劇下降),于是大量請求都會(huì)打到存儲(chǔ)層,存儲(chǔ)層的調(diào)用量會(huì)暴增.造成存儲(chǔ)層也會(huì)級(jí)聯(lián)宕機(jī)的情況。

2)解決方案

1.保證緩存層服務(wù)高可用性,比如使用哨兵模式和集群

2.依賴隔離組件為后端限流熔斷并降級(jí)。比如使用Sentinel或Hystrix限流降級(jí)組件。比如服務(wù)降級(jí),我們可以針對不同的數(shù)據(jù)采取不同的處理方式。當(dāng)業(yè)務(wù)應(yīng)用訪問的是非核心數(shù)據(jù)(例如電商商 品屬性,用戶信息等)時(shí),暫時(shí)停止從緩存中查詢這些數(shù)據(jù),而是直接返回預(yù)定義的默認(rèn)降級(jí)信息、空值或是 錯(cuò)誤提示信息;當(dāng)業(yè)務(wù)應(yīng)用訪問的是核心數(shù)據(jù)(例如電商商品庫存)時(shí),仍然允許查詢緩存,如果緩存缺失,也可以繼續(xù)通過數(shù)據(jù)庫讀取.

4.熱點(diǎn)緩存key重建優(yōu)化

開發(fā)人員使用“緩存+過期時(shí)間”的策略既可以加速數(shù)據(jù)讀寫, 又保證數(shù)據(jù)的定期更新, 這種模式基本能夠滿足絕大部分需求。 但是有兩個(gè)問題如果同時(shí)出現(xiàn),可能就會(huì)對應(yīng)用造成致命的危害:

  • 當(dāng)前key是一個(gè)熱點(diǎn)key并發(fā)量非常大。 (以板藍(lán)根為例,疫情之前不是熱點(diǎn)數(shù)據(jù),就沒有放在緩存中,但是聽說可以抗新冠,火了起來,此時(shí)這些請求打在數(shù)據(jù)庫上可能把數(shù)據(jù)庫直接搞宕機(jī))
  • 重建緩存不能在短時(shí)間完成, 可能是一個(gè)復(fù)雜計(jì)算, 例如復(fù)雜的SQL、 多次IO、 多個(gè)依賴等。

在緩存失效的瞬間,有大量線程來重建緩存,造成后端負(fù)載加大,甚至可能會(huì)讓應(yīng)用崩潰.

解決方案:

解決這個(gè)問題主要就是要避免大量線程同時(shí)重建緩存。我們可以利用互斥鎖來解決,此方法只允許一個(gè)線程重建緩存,其他線程等待重建緩存的線程執(zhí)行完,重新從

緩存獲取數(shù)據(jù)即可。

代碼示例:

String get(String key) {
    // 從Redis中獲取數(shù)據(jù)
    String value = redis.get(key);
    // 如果value為空, 則開始重構(gòu)緩存
    if (value == null){
        // 只允許一個(gè)線程重建緩存, 使用nx, 并設(shè)置過期時(shí)間ex
        String mutexKey = "mutext:key:" + key;
        if (redis.set(mutexKey, "1", "ex 180", "nx")){
            // 從數(shù)據(jù)源獲取數(shù)據(jù)
            value = db.get(key);
            // 回寫Redis, 并設(shè)置過期時(shí)間
            redis.setex(key, timeout, value);
            // 刪除key_mutex
            redis.delete(mutexKey);
        }else{
            Thread.sleep(50);
            get(key);
        }
    }
    return value;
}

5. 緩存和數(shù)據(jù)庫雙寫不一致

在大并發(fā)下,同時(shí)操作數(shù)據(jù)庫與緩存會(huì)存在數(shù)據(jù)不一致性問題

1、雙寫不一致問題

線程1寫完數(shù)據(jù)庫,還未更新緩存,線程2又寫數(shù)據(jù)庫,更新緩存,最好線程1又更新了緩存,此時(shí)造成數(shù)據(jù)庫和緩存中的數(shù)據(jù)我不一致的。

2、讀寫并發(fā)不一致

1.線程1 寫進(jìn)數(shù)據(jù)庫然后刪除緩存

2.線程3查詢緩存是空的,從數(shù)據(jù)庫中拿到數(shù)據(jù)但是還未放入緩存

3.線程2又進(jìn)來了寫數(shù)據(jù)庫并刪除了緩存

4.線程3繼續(xù)更新緩存 此時(shí)緩存和數(shù)據(jù)庫中的數(shù)據(jù)還是不一致的。

解決方案:

1、對于并發(fā)幾率很小的數(shù)據(jù)(如個(gè)人維度的訂單數(shù)據(jù)、用戶數(shù)據(jù)等),這種幾乎不用考慮這個(gè)問題,很少會(huì)發(fā)生緩存不一致,可以給緩存數(shù)據(jù)加上過期時(shí)間,每隔一段時(shí)間觸發(fā)讀的主動(dòng)更新即可。

2、就算并發(fā)很高,如果業(yè)務(wù)上能容忍短時(shí)間的緩存數(shù)據(jù)不一致(如商品名稱,商品分類菜單等),緩存加上過期時(shí)間依然可以解決大部分業(yè)務(wù)對于緩存的要求

3、如果不能容忍緩存數(shù)據(jù)不一致,可以通過加讀寫鎖保證并發(fā)讀寫或?qū)憣懙臅r(shí)候按順序排好隊(duì),讀讀的時(shí)候相 當(dāng)于無鎖

4、也可以用阿里開源的canal通過監(jiān)聽數(shù)據(jù)庫的binlog日志及時(shí)的去修改緩存,但是引入了新的中間件,增加了系統(tǒng)的復(fù)雜度。

6.Redis對過期key的三種清除策略

1)被動(dòng)刪除

當(dāng)讀/寫一個(gè)已經(jīng)過期的key時(shí),會(huì)觸發(fā)惰性刪除策略,直接刪除掉這個(gè)過期key

2)主動(dòng)刪除

由于惰性刪除策略無法保證冷數(shù)據(jù)被及時(shí)刪掉,所以Redis會(huì)定期主動(dòng)淘汰一批已過期的key

3)內(nèi)存淘汰

主動(dòng)清理策略在Redis 4.0 之前一共實(shí)現(xiàn)了 6 種內(nèi)存淘汰策略,在 4.0 之后,又增加了 2 種策 略,總共8種:1

a)針對設(shè)置了過期時(shí)間的key做處理:

  • volatile-ttl:在篩選時(shí),會(huì)針對設(shè)置了過期時(shí)間的鍵值對,根據(jù)過期時(shí)間的先后進(jìn)行刪 除,越早過期的越先被刪除。
  • volatile-random:就像它的名稱一樣,在設(shè)置了過期時(shí)間的鍵值對中,進(jìn)行隨機(jī)刪除。
  • volatile-lru:會(huì)使用 LRU 算法篩選設(shè)置了過期時(shí)間的鍵值對刪除。
  • volatile-lfu:會(huì)使用 LFU 算法篩選設(shè)置了過期時(shí)間的鍵值對刪除。

b) 針對所有的key做處理:

  • allkeys-random:從所有鍵值對中隨機(jī)選擇并刪除數(shù)據(jù)。
  • allkeys-lru:使用 LRU 算法在所有數(shù)據(jù)中進(jìn)行篩選刪除。
  • allkeys-lfu:使用 LFU 算法在所有數(shù)據(jù)中進(jìn)行篩選刪除。

c) 不處理:

  • noeviction:不會(huì)剔除任何數(shù)據(jù),拒絕所有寫入操作并返回客戶端錯(cuò)誤信息"(error) OOM command not allowed when used memory",此時(shí)Redis只響應(yīng)讀操作。

LRU 算法(Least Recently Used,最近最少使用)

淘汰很久沒被訪問過的數(shù)據(jù),以最近一次訪問時(shí)間作為參考。

LFU 算法(Least Frequently Used,最不經(jīng)常使用)

淘汰最近一段時(shí)間被訪問次數(shù)最少的數(shù)據(jù),以次數(shù)作為參考。

如何選擇主動(dòng)清理策略?

當(dāng)存在熱點(diǎn)數(shù)據(jù)時(shí),LRU的效率很好,但偶發(fā)性的、周期性的批量操作(一個(gè)不常用的key被突然訪問了一下,此時(shí)訪問時(shí)間比一些熱點(diǎn)key要晚,導(dǎo)致熱點(diǎn)key被清理)會(huì)導(dǎo)致LRU命中率急劇下降,緩存污染情況比較嚴(yán)重。這時(shí)使用LFU可能更好點(diǎn)。

根據(jù)自身業(yè)務(wù)類型,配置好maxmemory-policy(默認(rèn)是noeviction),推薦使用volatile-lru。如 果不設(shè)置最大內(nèi)存,當(dāng) Redis 內(nèi)存超出物理內(nèi)存限制時(shí),內(nèi)存的數(shù)據(jù)會(huì)開始和磁盤產(chǎn)生頻繁的交換 (swap),會(huì)讓 Redis 的性能急劇下降。當(dāng)Redis運(yùn)行在主從模式時(shí),只有主結(jié)點(diǎn)才會(huì)執(zhí)行過期刪除策略,然后把刪除操作”del ,key”同步到從結(jié)點(diǎn)刪除數(shù)據(jù)

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Redis中List實(shí)現(xiàn)雙鏈表

    Redis中List實(shí)現(xiàn)雙鏈表

    本文主要介紹了Redis中List實(shí)現(xiàn)雙鏈表,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Redis鎖完美解決高并發(fā)秒殺問題

    Redis鎖完美解決高并發(fā)秒殺問題

    本文主要介紹了Redis鎖完美解決高并發(fā)秒殺問題,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • redis秒殺系統(tǒng)的實(shí)現(xiàn)

    redis秒殺系統(tǒng)的實(shí)現(xiàn)

    秒殺在很多活動(dòng)大促中都可以用到,本文主要介紹了redis秒殺系統(tǒng)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • redis yml配置的用法小結(jié)

    redis yml配置的用法小結(jié)

    RedisYML配置是Redis的一種配置文件格式,,對Redis的配置進(jìn)行統(tǒng)一管理,本文就來介紹了redis yml配置的用法小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • Redis基本數(shù)據(jù)類型String常用操作命令

    Redis基本數(shù)據(jù)類型String常用操作命令

    這篇文章主要為大家介紹了Redis基本數(shù)據(jù)類型String常用操作命令,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • Redis特殊數(shù)據(jù)類型HyperLogLog基數(shù)統(tǒng)計(jì)算法講解

    Redis特殊數(shù)據(jù)類型HyperLogLog基數(shù)統(tǒng)計(jì)算法講解

    這篇文章主要為大家介紹了Redis特殊數(shù)據(jù)類型HyperLogLog基數(shù)統(tǒng)計(jì)算法講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • 詳解redis desktop manager安裝及連接方式

    詳解redis desktop manager安裝及連接方式

    這篇文章主要介紹了redis desktop manager安裝及連接方式,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Redis高性能的原因及說明

    Redis高性能的原因及說明

    這篇文章主要介紹了Redis高性能的原因及說明,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • redis加鎖的幾種方式匯總

    redis加鎖的幾種方式匯總

    這篇文章主要介紹了redis加鎖的幾種方式匯總,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Redis list 類型學(xué)習(xí)筆記與總結(jié)

    Redis list 類型學(xué)習(xí)筆記與總結(jié)

    這篇文章主要介紹了Redis list 類型學(xué)習(xí)筆記與總結(jié),本文著重講解了關(guān)于List的一些常用方法,比如lpush 方法、lrange 方法、rpush 方法、linsert 方法、 lset 方法等,需要的朋友可以參考下
    2015-06-06

最新評論