最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis解決緩存雪崩、穿透和擊穿的問(wèn)題(Redis使用必看)

 更新時(shí)間:2023年08月15日 09:51:26   作者:我有一顆五葉草  
這篇文章主要給大家介紹了Redis解決緩存雪崩、緩存穿透、緩存擊穿的解決方案,文中有詳細(xì)的圖文介紹,具有一定的參考價(jià)值,需要的朋友可以參考下

緩存擊穿

緩存擊穿問(wèn)題也叫熱點(diǎn)Key問(wèn)題,就是一個(gè)被高并發(fā)訪問(wèn)并且緩存重建業(yè)務(wù)較復(fù)雜的key突然失效了,無(wú)數(shù)的請(qǐng)求訪問(wèn)會(huì)在瞬間給數(shù)據(jù)庫(kù)帶來(lái)巨大的沖擊。常見(jiàn)的解決方案有:

- 互斥鎖
	- 邏輯過(guò)期
	- key 永不過(guò)期
	- 接口限流

邏輯分析:假設(shè)線程1在查詢緩存之后,本來(lái)應(yīng)該去查詢數(shù)據(jù)庫(kù),然后把這個(gè)數(shù)據(jù)重新加載到緩存的,此時(shí)只要線程1走完這個(gè)邏輯,其他線程就都能從緩存中加載這些數(shù)據(jù)了,但是假設(shè)在線程1沒(méi)有走完的時(shí)候,后續(xù)的線程2,線程3,線程4同時(shí)過(guò)來(lái)訪問(wèn)當(dāng)前這個(gè)方法, 那么這些線程都不能從緩存中查詢到數(shù)據(jù),那么他們就會(huì)同一時(shí)刻來(lái)訪問(wèn)查詢緩存,都沒(méi)查到,接著同一時(shí)間去訪問(wèn)數(shù)據(jù)庫(kù),同時(shí)的去執(zhí)行數(shù)據(jù)庫(kù)代碼,對(duì)數(shù)據(jù)庫(kù)訪問(wèn)壓力非常大。

解決方案一、使用鎖來(lái)解決:

因?yàn)殒i能實(shí)現(xiàn)互斥性。假設(shè)線程過(guò)來(lái),只能一個(gè)人一個(gè)人的來(lái)訪問(wèn)數(shù)據(jù)庫(kù),從而避免對(duì)于數(shù)據(jù)庫(kù)訪問(wèn)壓力過(guò)大,但這也會(huì)影響查詢的性能,因?yàn)榇藭r(shí)會(huì)讓查詢的性能從并行變成了串行,我們可以采用 tryLock 方法 + double check 來(lái)解決這樣的問(wèn)題。假設(shè)現(xiàn)在線程1過(guò)來(lái)訪問(wèn),他查詢緩存沒(méi)有命中,但是此時(shí)他獲得到了鎖的資源,那么線程1就會(huì)一個(gè)人去執(zhí)行邏輯,假設(shè)現(xiàn)在線程2過(guò)來(lái),線程2在執(zhí)行過(guò)程中,并沒(méi)有獲得到鎖,那么線程2就可以進(jìn)行到休眠,直到線程1把鎖釋放后,線程2獲得到鎖,然后再來(lái)執(zhí)行邏輯,此時(shí)就能夠從緩存中拿到數(shù)據(jù)了。

1653328288627.png

解決方案二、邏輯過(guò)期方案

方案分析:我們之所以會(huì)出現(xiàn)這個(gè)緩存擊穿問(wèn)題,主要原因是在于我們對(duì)key設(shè)置了過(guò)期時(shí)間,假設(shè)我們不設(shè)置過(guò)期時(shí)間,其實(shí)就不會(huì)有緩存擊穿的問(wèn)題,但是不設(shè)置過(guò)期時(shí)間,這樣數(shù)據(jù)不就一直占用我們內(nèi)存了嗎,我們可以采用邏輯過(guò)期方案。我們把過(guò)期時(shí)間設(shè)置在 redis的value中,注意:這個(gè)過(guò)期時(shí)間并不會(huì)直接作用于redis,而是我們后續(xù)通過(guò)邏輯去處理。假設(shè)線程1去查詢緩存,然后從value中判斷出來(lái)當(dāng)前的數(shù)據(jù)已經(jīng)過(guò)期了,此時(shí)線程1去獲得互斥鎖,那么其他線程會(huì)進(jìn)行阻塞,獲得了鎖的線程他會(huì)開(kāi)啟一個(gè) 線程去進(jìn)行 以前的重構(gòu)數(shù)據(jù)的邏輯,直到新開(kāi)的線程完成這個(gè)邏輯后,才釋放鎖, 而線程1直接進(jìn)行返回,假設(shè)現(xiàn)在線程3過(guò)來(lái)訪問(wèn),由于線程線程2持有著鎖,所以線程3無(wú)法獲得鎖,線程3也直接返回?cái)?shù)據(jù),只有等到新開(kāi)的線程2把重建數(shù)據(jù)構(gòu)建完后,其他線程才能走返回正確的數(shù)據(jù)。這種方案巧妙在于,異步的構(gòu)建緩存,缺點(diǎn)在于在構(gòu)建完緩存之前,返回的都是臟數(shù)據(jù)。

1653328663897.png

進(jìn)行對(duì)比互斥鎖方案由于保證了互斥性,所以數(shù)據(jù)一致,且實(shí)現(xiàn)簡(jiǎn)單,因?yàn)閮H僅只需要加一把鎖而已,也沒(méi)其他的事情需要操心,所以沒(méi)有額外的內(nèi)存消耗,缺點(diǎn)在于有鎖就有死鎖問(wèn)題的發(fā)生,且只能串行執(zhí)行性能肯定受到影響邏輯過(guò)期方案: 線程讀取過(guò)程中不需要等待,性能好,有一個(gè)額外的線程持有鎖去進(jìn)行重構(gòu)數(shù)據(jù),但是在重構(gòu)數(shù)據(jù)完成前,其他的線程只能返回之前的數(shù)據(jù),且實(shí)現(xiàn)起來(lái)麻煩。

1653357522914.png

解決方案三、永不過(guò)期 主動(dòng)更新

解決方案四、接口限流

利用互斥鎖解決緩存擊穿問(wèn)題

核心思路:相較于原來(lái)從緩存中查詢不到數(shù)據(jù)后直接查詢數(shù)據(jù)庫(kù)而言,現(xiàn)在的方案是 進(jìn)行查詢之后,如果從緩存沒(méi)有查詢到數(shù)據(jù),則進(jìn)行互斥鎖的獲取,獲取互斥鎖后,判斷是否獲得到了鎖,如果沒(méi)有獲得到,則休眠,過(guò)一會(huì)再進(jìn)行嘗試,直到獲取到鎖為止,才能進(jìn)行查詢?nèi)绻@取到了鎖的線程,再去進(jìn)行查詢,查詢后將數(shù)據(jù)寫入redis,再釋放鎖,返回?cái)?shù)據(jù),利用互斥鎖就能保證只有一個(gè)線程去執(zhí)行操作數(shù)據(jù)庫(kù)的邏輯,防止緩存擊穿。

1653357860001.png

操作鎖的代碼:核心思路就是利用 redis 的 setnx 方法來(lái)表示獲取鎖,該方法含義是redis中如果沒(méi)有這個(gè) key,則插入成功,返回1,在 stringRedisTemplate 中返回 true, 如果有這個(gè) key 則插入失敗,則返回0,在stringRedisTemplate 返回 false,我們可以通過(guò) true,或者是 false,來(lái)表示是否有線程成功插入 key,成功插入的 key 的線程我們認(rèn)為他就是獲得到鎖的線程。

private boolean tryLock(String key, String value, long time) {
    Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(flag);
}
private void unlock(String key) {
    stringRedisTemplate.delete(key);
}

操作鎖要注意對(duì) value 加入標(biāo)識(shí),在釋放鎖之前對(duì)其進(jìn)行判斷是不是自己的鎖,防止誤刪?。ㄟ€要保證判斷語(yǔ)句和釋放語(yǔ)句的原子性 可以用 lua 腳本)

核心代碼:

public Shop queryWithMutex(Long id)  {
    String key = CACHE_SHOP_KEY + id;
    // 1、從redis中查詢商鋪緩存
    String shopJson = stringRedisTemplate.opsForValue().get("key");
    // 2、判斷是否存在
    if (StrUtil.isNotBlank(shopJson)) {
        // 存在,直接返回
        return JSONUtil.toBean(shopJson, Shop.class);
    }
    //判斷命中的值是否是空值
    if (shopJson != null) {
        //返回一個(gè)錯(cuò)誤信息
        return null;
    }
    // 4.實(shí)現(xiàn)緩存重構(gòu)
    //4.1 獲取互斥鎖
    String lockKey = "lock:shop:" + id;
	long current_thread_id = Thread.currentThread().getId();
    Shop shop = null;
    try {
        boolean isLock = tryLock(lockKey, current_thread_id, 10);
        // 4.2 判斷否獲取成功
        if(!isLock){
            //4.3 失敗,則休眠重試
            Thread.sleep(50);
            return queryWithMutex(id);
        }
        //4.4 成功,根據(jù)id查詢數(shù)據(jù)庫(kù)
         shop = getById(id);
        // 5.不存在,返回錯(cuò)誤
        if(shop == null){
             //將空值寫入redis
            stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
            //返回錯(cuò)誤信息
            return null;
        }
        //6.寫入redis
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_NULL_TTL,TimeUnit.MINUTES);
    }catch (Exception e){
        throw new RuntimeException(e);
    }
    finally {
        //7.釋放互斥鎖
        Object o = stringRedisTemplate.opsForValue().get(key);
        if(o != null && (String)o.equals(current_thread_id)){
            unlock(lockKey);
        }
    }
    return shop;
}

利用邏輯過(guò)期解決緩存擊穿問(wèn)題

緩存穿透

緩存穿透 :緩存穿透是指客戶端請(qǐng)求的數(shù)據(jù)在緩存中和數(shù)據(jù)庫(kù)中都不存在,這樣緩存永遠(yuǎn)不會(huì)生效,這些請(qǐng)求都會(huì)打到數(shù)據(jù)庫(kù)。

常見(jiàn)的解決方案有以下幾種:

  • 緩存空對(duì)象
    • 優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,維護(hù)方便
    • 缺點(diǎn):
      • 額外的內(nèi)存消耗
      • 可能造成短期的不一致
  • 布隆過(guò)濾
    • 優(yōu)點(diǎn):內(nèi)存占用較少,沒(méi)有多余key
    • 缺點(diǎn):
      • 實(shí)現(xiàn)復(fù)雜
      • 存在誤判可能
  • id 格式校驗(yàn)

緩存空對(duì)象

思路分析

當(dāng)我們客戶端訪問(wèn)不存在的數(shù)據(jù)時(shí),先請(qǐng)求redis,但是此時(shí)redis中沒(méi)有數(shù)據(jù),此時(shí)會(huì)訪問(wèn)到數(shù)據(jù)庫(kù),但是數(shù)據(jù)庫(kù)中也沒(méi)有數(shù)據(jù),這個(gè)數(shù)據(jù)穿透了緩存,直擊數(shù)據(jù)庫(kù)。因?yàn)閿?shù)據(jù)庫(kù)能夠承載的并發(fā)不如 redis 這么高,如果大量的請(qǐng)求同時(shí)過(guò)來(lái)訪問(wèn)這種不存在的數(shù)據(jù),這些請(qǐng)求就都會(huì)訪問(wèn)到數(shù)據(jù)庫(kù),簡(jiǎn)單的解決方案就是哪怕這個(gè)數(shù)據(jù)在數(shù)據(jù)庫(kù)中也不存在,我們也把這個(gè)數(shù)據(jù)存入到 redis 中去,這樣,下次用戶過(guò)來(lái)訪問(wèn)這個(gè)不存在的數(shù)據(jù),那么在redis中也能找到這個(gè)數(shù)據(jù)就不會(huì)進(jìn)入到緩存了。

布隆過(guò)濾

我們可以將數(shù)據(jù)庫(kù)的數(shù)據(jù),所對(duì)應(yīng)的id寫入到一個(gè)list集合中,當(dāng)用戶過(guò)來(lái)訪問(wèn)的時(shí)候,我們直接去判斷l(xiāng)ist中是否包含當(dāng)前的要查詢的數(shù)據(jù),如果說(shuō)用戶要查詢的id數(shù)據(jù)并不在list集合中,則直接返回,如果list中包含對(duì)應(yīng)查詢的id數(shù)據(jù),則說(shuō)明不是一次緩存穿透數(shù)據(jù),則直接放行。

1653836416586.png

現(xiàn)在的問(wèn)題是這個(gè)主鍵其實(shí)并沒(méi)有那么短,而是很長(zhǎng)的一個(gè) 主鍵哪怕你單獨(dú)去提取這個(gè)主鍵,但是在11年左右,淘寶的商品總量就已經(jīng)超過(guò)10億個(gè)所以如果采用以上方案,這個(gè)list也會(huì)很大,所以我們可以使用bitmap來(lái)減少list的存儲(chǔ)空間我們可以把list數(shù)據(jù)抽象成一個(gè)非常大的bitmap,我們不再使用list,而是將db中的id數(shù)據(jù)利用哈希思想,比如:id % bitmap.size = 算出當(dāng)前這個(gè)id對(duì)應(yīng)應(yīng)該落在bitmap的哪個(gè)索引上,然后將這個(gè)值從0變成1,然后當(dāng)用戶來(lái)查詢數(shù)據(jù)時(shí),此時(shí)已經(jīng)沒(méi)有了list,讓用戶用他查詢的id去用相同的哈希算法, 算出來(lái)當(dāng)前這個(gè)id應(yīng)當(dāng)落在bitmap的哪一位,然后判斷這一位是0,還是1,如果是0則表明這一位上的數(shù)據(jù)一定不存在, 采用這種方式來(lái)處理,需要重點(diǎn)考慮一個(gè)事情,就是誤差率,所謂的誤差率就是指當(dāng)發(fā)生哈希沖突的時(shí)候,產(chǎn)生的誤差。

1653836578970.png

id 格式校驗(yàn)

將客戶端傳來(lái)的 id 做校驗(yàn)比如:

if(id < 1 || id > Integer.MIN_VALUE){
	return null;
}

具體校驗(yàn)根據(jù)業(yè)務(wù)來(lái)。

緩存雪崩

緩存雪崩是指在同一時(shí)段大量的緩存key同時(shí)失效或者Redis服務(wù)宕機(jī),導(dǎo)致大量請(qǐng)求到達(dá)數(shù)據(jù)庫(kù),帶來(lái)巨大壓力。

緩存雪崩

解決方案

  • 給不同的Key的TTL添加隨機(jī)值
  • 利用Redis集群提高服務(wù)的可用性
  • 給緩存業(yè)務(wù)添加降級(jí)限流策略
  • 給業(yè)務(wù)添加多級(jí)緩存

以上就是Redis解決緩存雪崩、穿透和擊穿的問(wèn)題(Redis使用必看)的詳細(xì)內(nèi)容,更多關(guān)于Redis解決緩存雪崩、穿透和擊穿的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Redis官方可視化工具RedisInsight的安裝使用詳細(xì)教程(功能強(qiáng)大)

    Redis官方可視化工具RedisInsight的安裝使用詳細(xì)教程(功能強(qiáng)大)

    RedisInsight是Redis官方出品的可視化管理工具,可用于設(shè)計(jì)、開(kāi)發(fā)、優(yōu)化你的Redis應(yīng)用。支持深色和淺色兩種主題,界面非常炫酷,接下來(lái)通過(guò)本文給大家介紹Redis官方可視化工具RedisInsight的安裝使用過(guò)程,需要的朋友可以參考下
    2022-04-04
  • 淺析Redis?切片集群的數(shù)據(jù)傾斜問(wèn)題

    淺析Redis?切片集群的數(shù)據(jù)傾斜問(wèn)題

    如果?Redis?中的部署,采用的是切片集群,數(shù)據(jù)是會(huì)按照一定的規(guī)則分散到不同的實(shí)例中保存,比如,使用?Redis?Cluster?或?Codis,這篇文章主要介紹了Redis?切片集群的數(shù)據(jù)傾斜分析,需要的朋友可以參考下
    2022-06-06
  • Windows下Redis的安裝使用圖解

    Windows下Redis的安裝使用圖解

    Redis是一個(gè)key-value存儲(chǔ)系統(tǒng)。Redis的出現(xiàn),很大程度補(bǔ)償了memcached這類key/value存儲(chǔ)的不足,在部分場(chǎng)合可以對(duì)關(guān)系數(shù)據(jù)庫(kù)起到很好的補(bǔ)充作用。這篇文章小編為大家分享了在Windows下進(jìn)行安裝和使用Redis的技巧。
    2015-09-09
  • Redis安裝與使用方法小結(jié)

    Redis安裝與使用方法小結(jié)

    這篇文章主要介紹了Redis安裝與使用方法,結(jié)合實(shí)例形式分析了Redis數(shù)據(jù)庫(kù)的下載、安裝、啟動(dòng)、設(shè)置及相關(guān)使用操作注意事項(xiàng),需要的朋友可以參考下
    2018-04-04
  • 深入淺析Redis 集群伸縮原理

    深入淺析Redis 集群伸縮原理

    Redis 集群提供了靈活的節(jié)點(diǎn)擴(kuò)容和收縮方案。在不影響集群對(duì)外服務(wù)的情況下,可以為集群添加節(jié)點(diǎn)進(jìn)行擴(kuò)容,也可以下線部分節(jié)點(diǎn)進(jìn)行縮容,接下來(lái)通過(guò)本文給大家分享Redis 集群伸縮原理,感興趣的朋友一起看看吧
    2021-05-05
  • redis實(shí)現(xiàn)分布式延時(shí)隊(duì)列的示例代碼

    redis實(shí)現(xiàn)分布式延時(shí)隊(duì)列的示例代碼

    延時(shí)隊(duì)列是一種特殊的消息隊(duì)列,它允許將消息在一定的延遲時(shí)間后再進(jìn)行消費(fèi),延時(shí)隊(duì)列的實(shí)現(xiàn)方式可以有多種,本文主要來(lái)介紹一種redis實(shí)現(xiàn)的分布式延時(shí)隊(duì)列,希望對(duì)大家有所幫助
    2023-10-10
  • redis常用命令整理

    redis常用命令整理

    在本篇文章里小編給大家整理的是關(guān)于redis常用命令整理相關(guān)內(nèi)容需要的朋友們可以學(xué)習(xí)下。
    2020-03-03
  • 詳解緩存穿透擊穿雪崩解決方案

    詳解緩存穿透擊穿雪崩解決方案

    在我們?nèi)粘5拈_(kāi)發(fā)中,有時(shí)需要系統(tǒng)在極短的時(shí)間內(nèi)完成成千上萬(wàn)次的讀/寫操作,這個(gè)時(shí)候不是數(shù)據(jù)庫(kù)能夠承受的,通常會(huì)引入NoSQL技術(shù)。redis技術(shù)就是NoSQL技術(shù)中的一種,但是引入redis又有可能出現(xiàn)緩存穿透,緩存擊穿,緩存雪崩等問(wèn)題。本文就對(duì)這三種問(wèn)題進(jìn)行較深入剖析。
    2021-05-05
  • redis基本安裝判斷、啟動(dòng)使用方法示例

    redis基本安裝判斷、啟動(dòng)使用方法示例

    這篇文章主要介紹了redis基本安裝判斷、啟動(dòng)使用方法,結(jié)合實(shí)例形式分析了Redis針對(duì)是否安裝的判斷、啟動(dòng)等使用方法,需要的朋友可以參考下
    2020-02-02
  • 阿里云官方Redis開(kāi)發(fā)規(guī)范總結(jié)

    阿里云官方Redis開(kāi)發(fā)規(guī)范總結(jié)

    本文主要介紹了阿里云官方Redis開(kāi)發(fā)規(guī)范總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08

最新評(píng)論

黎川县| 常山县| 大名县| 浪卡子县| 武胜县| 屯昌县| 明溪县| 曲阳县| 财经| 宜章县| 吐鲁番市| 北碚区| 汾西县| 邵阳市| 眉山市| 冷水江市| 普洱| 康平县| 丹东市| 根河市| 东乡县| 汶上县| 鲁山县| 泸溪县| 繁昌县| 浏阳市| 嘉鱼县| 封丘县| 江源县| 静海县| 屏东市| 天等县| 益阳市| 靖江市| 长丰县| 湟源县| 澳门| 临夏县| 特克斯县| 横山县| 会昌县|