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

Redis解決緩存擊穿問(wèn)題的兩種方法

 更新時(shí)間:2025年03月21日 11:21:38   作者:打死不學(xué)Java代碼  
緩存擊穿問(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)巨大的沖擊,本文給大家介紹了Redis解決緩存擊穿問(wèn)題的兩種方法,需要的朋友可以參考下

引言

緩存擊穿:給某一個(gè)key設(shè)置了過(guò)期時(shí)間,當(dāng)key過(guò)期的時(shí)候,恰好這個(gè)時(shí)間點(diǎn)對(duì)這個(gè)key有大量的并發(fā)請(qǐng)求過(guò)來(lái),這些并發(fā)的請(qǐng)求可能會(huì)瞬間把DB壓垮

解決辦法

互斥鎖(強(qiáng)一致,性能差)

根據(jù)圖片就可以看出,我們的思路就是只能讓一個(gè)線程能夠進(jìn)行訪問(wèn)Redis,要想實(shí)現(xiàn)這個(gè)功能,我們也可以使用Redis自帶的setnx

封裝兩個(gè)方法,一個(gè)寫(xiě)key來(lái)嘗試獲取鎖另一個(gè)刪key來(lái)釋放鎖

/**
 * 嘗試獲取鎖
 *
 * @param key
 * @return
 */
private boolean tryLock(String key) {
    Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(flag);
}
 
/**
 * 釋放鎖
 *
 * @param key
 */
private void unlock(String key) {
    stringRedisTemplate.delete(key);
}

在并行情況下每當(dāng)其他線程想要獲取鎖,來(lái)訪問(wèn)緩存都要通過(guò)將自己的key寫(xiě)到tryLock()方法里,setIfAbsent()返回false則說(shuō)明有線程在在更新緩存數(shù)據(jù),鎖未釋放。若返回true則說(shuō)明當(dāng)前線程拿到鎖了可以訪問(wèn)緩存甚至操作緩存。

我們?cè)谙旅嬉粋€(gè)熱門(mén)的查詢場(chǎng)景中用代碼用代碼來(lái)實(shí)現(xiàn)互斥鎖解決緩存擊穿,代碼如下:

    /**
     * 解決緩存擊穿的互斥鎖
     * @param id
     * @return
     */
    public Shop queryWithMutex(Long id) {
        String key = CACHE_SHOP_KEY + id;
        //1.從Redis查詢緩存
        String shopJson = stringRedisTemplate.opsForValue().get(key);  //JSON格式
        //2.判斷是否存在
        if (StrUtil.isNotBlank(shopJson)) { //不為空就返回 此工具類API會(huì)判斷" "為false
            //存在則直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            //return Result.ok(shop);
            return shop;
        }
        //3.判斷是否為空值 這里過(guò)濾 " "的情況,不用擔(dān)心會(huì)一直觸發(fā)這個(gè)條件因?yàn)樗蠺TL
        if (shopJson != null) {
            //返回一個(gè)空值
            return null;
        }
        //4.緩存重建 Redis中值為null的情況
        //4.1獲得互斥鎖
        String lockKey = "lock:shop"+id;
        Shop shopById=null;
        try {
            boolean isLock = tryLock(lockKey);
            //4.2判斷是否獲取成功
            if (!isLock){
                //4.3失敗,則休眠并重試
                Thread.sleep(50);
               return queryWithMutex(id);
            }
            //4.4成功,根據(jù)id查詢數(shù)據(jù)庫(kù)
            shopById = getById(id);
            //5.不存在則返回錯(cuò)誤
            if (shopById == null) {
                //將空值寫(xiě)入Redis
                stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
                //為什么這里要存一個(gè)" "這是因?yàn)槿绻罄m(xù)DB中有數(shù)據(jù)補(bǔ)充的話還可以去重建緩存
                //return Result.fail("暫無(wú)該商鋪信息");
                return null;
            }
            //6.存在,寫(xiě)入Redis
            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shopById), CACHE_SHOP_TTL, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            //7.釋放互斥鎖
            unlock(lockKey);
        }
 
        return shopById;
    }

邏輯過(guò)期(高可用,性能優(yōu))

方案:用戶查詢某個(gè)熱門(mén)產(chǎn)品信息,如果緩存未命中(即信息為空),則直接返回空,不去查詢數(shù)據(jù)庫(kù)。如果緩存信息命中,則判斷是否邏輯過(guò)期,未過(guò)期返回緩存信息,過(guò)期則重建緩存,嘗試獲得互斥鎖,獲取失敗則直接返回已過(guò)期緩存數(shù)據(jù),獲取成功則開(kāi)啟獨(dú)立線程去重構(gòu)緩存然后直接返回舊的緩存信息,重構(gòu)完成之后就釋放互斥鎖。

封裝一個(gè)方法用來(lái)模擬更新邏輯過(guò)期時(shí)間與緩存的數(shù)據(jù)在測(cè)試類里運(yùn)行起來(lái)達(dá)到數(shù)據(jù)與熱的效果 

/**
 * 添加邏輯過(guò)期時(shí)間
 *
 * @param id
 * @param expireTime
 */
public void saveShopRedis(Long id, Long expireTime) {
    //查詢店鋪信息
    Shop shop = getById(id);
    //封裝邏輯過(guò)期時(shí)間
    RedisData redisData = new RedisData();
    redisData.setData(shop);
    redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireTime));
    //將封裝過(guò)期時(shí)間和商鋪數(shù)據(jù)的對(duì)象寫(xiě)入Redis
    stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(redisData));
}

查詢接口:

/**
 * 邏輯過(guò)期解決緩存擊穿
 *
 * @param id
 * @return
 */
public Shop queryWithLogicalExpire(Long id) throws InterruptedException {
    String key = CACHE_SHOP_KEY + id;
    Thread.sleep(200);
    //1.從Redis查詢緩存
    String shopJson = stringRedisTemplate.opsForValue().get(key);  //JSON格式
    //2.判斷是否存在
    if (StrUtil.isBlank(shopJson)) {
        //不存在則直接返回
        return null;
    }
    //3.判斷是否為空值
    if (shopJson != null) {
        //返回一個(gè)空值
        //return Result.fail("店鋪不存在!");
        return null;
    }
    //4.命中
    //4.1將JSON反序列化為對(duì)象
    RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);
    Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);
    LocalDateTime expireTime = redisData.getExpireTime();
    //4.2判斷是否過(guò)期
    if (expireTime.isAfter(LocalDateTime.now())) {
        //5.未過(guò)期則返回店鋪信息
        return shop;
    }
    //6.過(guò)期則緩存重建
    //6.1獲取互斥鎖
    String LockKey = LOCK_SHOP_KEY + id;
    boolean isLock = tryLock(LockKey);
    //6.2判斷是否成功獲得鎖
    if (isLock) {
        //6.3成功,開(kāi)啟獨(dú)立線程,實(shí)現(xiàn)緩存重建
        CACHE_REBUILD_EXECUTOR.submit(() -> {
            try {
                //重建緩存
                this.saveShop2Redis(id, 20L);
 
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                //釋放鎖
                unlock(LockKey);
            }
        });
    }
    //6.4返回商鋪信息
    return shop;
}

設(shè)計(jì)邏輯過(guò)期時(shí)間

可以用這個(gè)方法設(shè)置邏輯過(guò)期時(shí)間

import org.redisson.Redisson;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
 
public class RedissonExample {
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
 
        RedissonClient redisson = Redisson.create(config);
 
        String key = "exampleKey";
        String value = "exampleValue";
        int timeout = 10; // 過(guò)期時(shí)間(秒)
 
        // 獲取RBucket對(duì)象
        RBucket<String> bucket = redisson.getBucket(key);
 
        // 設(shè)置值并指定過(guò)期時(shí)間
        bucket.set(value, timeout, TimeUnit.SECONDS);
 
        System.out.println("設(shè)置成功");
        redisson.shutdown();
    }
}

大家可以看到,邏輯過(guò)期鎖就是可以實(shí)現(xiàn)并發(fā),所以他的效率更快,性能更好

但是

  • 犧牲了數(shù)據(jù)的實(shí)時(shí)性,以保證高并發(fā)場(chǎng)景下的服務(wù)可用性和數(shù)據(jù)庫(kù)的穩(wěn)定性。

  • 在實(shí)際應(yīng)用中,需要確保獲取互斥鎖的操作是原子的,并且鎖具有合適的超時(shí)時(shí)間,以避免死鎖的發(fā)生。

  • 邏輯過(guò)期策略適用于那些對(duì)數(shù)據(jù)實(shí)時(shí)性要求不高,但要求服務(wù)高可用性的場(chǎng)景。

到此這篇關(guān)于Redis解決緩存擊穿問(wèn)題的兩種方法的文章就介紹到這了,更多相關(guān)Redis解決緩存擊穿內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Deepin UOS編譯安裝Redis的實(shí)現(xiàn)步驟

    Deepin UOS編譯安裝Redis的實(shí)現(xiàn)步驟

    本文主要介紹了Deepin UOS編譯安裝Redis的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Redis基于Session實(shí)現(xiàn)分布式登錄的示例代碼

    Redis基于Session實(shí)現(xiàn)分布式登錄的示例代碼

    本文主要介紹了Redis基于Session實(shí)現(xiàn)分布式登錄的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-08-08
  • 關(guān)于redis狀態(tài)監(jiān)控和性能調(diào)優(yōu)詳解

    關(guān)于redis狀態(tài)監(jiān)控和性能調(diào)優(yōu)詳解

    Redis是一種高級(jí)key-value數(shù)據(jù)庫(kù)。它跟memcached類似,不過(guò)數(shù)據(jù)可以持久化,而且支持的數(shù)據(jù)類型很豐富。有字符串,鏈表、哈希、集合和有序集合5種。下面這篇文章主要給大家介紹了關(guān)于redis狀態(tài)監(jiān)控和性能調(diào)優(yōu)的相關(guān)資料,需要的朋友可以參考下。
    2017-09-09
  • 查詢?cè)L問(wèn)Redis特定數(shù)據(jù)庫(kù)的客戶端IP的操作指南

    查詢?cè)L問(wèn)Redis特定數(shù)據(jù)庫(kù)的客戶端IP的操作指南

    在Redis運(yùn)維和開(kāi)發(fā)過(guò)程中,了解哪些客戶端連接并訪問(wèn)了特定的數(shù)據(jù)庫(kù)(如db0)是一個(gè)常見(jiàn)的需求,然而,Redis本身并不直接提供歷史訪問(wèn)日志,因此我們需要借助一些內(nèi)置命令和外部工具來(lái)實(shí)現(xiàn)這一目標(biāo),本文將詳細(xì)介紹如何查詢?cè)L問(wèn)Redis特定數(shù)據(jù)庫(kù)(如db0)的客戶端IP
    2025-06-06
  • redis 交集、并集、差集的具體使用

    redis 交集、并集、差集的具體使用

    這篇文章主要介紹了redis 交集、并集、差集的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • Redis入門(mén)基礎(chǔ)常用操作命令整理

    Redis入門(mén)基礎(chǔ)常用操作命令整理

    這篇文章主要為大家介紹了Redis入門(mén)基礎(chǔ)常用操作命令的整理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • redis的常見(jiàn)用法和問(wèn)題之緩存擊穿、緩存穿透、緩存雪崩詳解

    redis的常見(jiàn)用法和問(wèn)題之緩存擊穿、緩存穿透、緩存雪崩詳解

    在現(xiàn)代高并發(fā)的互聯(lián)網(wǎng)應(yīng)用中,緩存技術(shù)已成為提升系統(tǒng)響應(yīng)速度與減輕后端數(shù)據(jù)庫(kù)壓力的關(guān)鍵手段,這篇文章主要介紹了redis的常見(jiàn)用法和問(wèn)題之緩存擊穿、緩存穿透、緩存雪崩的相關(guān)資料,需要的朋友可以參考下
    2026-05-05
  • Redis密碼在springboot中自定義加解密實(shí)踐

    Redis密碼在springboot中自定義加解密實(shí)踐

    文章介紹了在Spring?Boot項(xiàng)目中自定義配置Redis密碼的加解密方法,通過(guò)在`application.yml`文件中配置信息,并在`RedisConfig`類中實(shí)現(xiàn)加解密邏輯
    2026-01-01
  • redis分布式鎖優(yōu)化的實(shí)現(xiàn)

    redis分布式鎖優(yōu)化的實(shí)現(xiàn)

    本文主要介紹了redis分布式鎖優(yōu)化的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 虛擬機(jī)下的Redis無(wú)法訪問(wèn)報(bào)錯(cuò)500解決方法

    虛擬機(jī)下的Redis無(wú)法訪問(wèn)報(bào)錯(cuò)500解決方法

    這篇文章主要介紹了虛擬機(jī)下的Redis無(wú)法訪問(wèn),報(bào)錯(cuò)500解決方法,由于我的redis是在虛擬機(jī)下安裝的,無(wú)法訪問(wèn)redis的原因是因?yàn)樘摂M機(jī)的ip地址和主機(jī)不同,文中通過(guò)圖文結(jié)合給出了詳細(xì)的解決方法,需要的朋友可以參考下
    2024-02-02

最新評(píng)論

洞头县| 平潭县| 洞口县| 昌都县| 敦煌市| 平泉县| 宁津县| 湛江市| 瑞安市| 镇平县| 义乌市| 栾川县| 曲水县| 张家川| 天台县| 莫力| 宜都市| 泰安市| 日土县| 桃江县| 荔波县| 额尔古纳市| 正镶白旗| 绥德县| 任丘市| 洛宁县| 淄博市| 阿合奇县| 福清市| 宁都县| 新沂市| 壤塘县| 平南县| 温泉县| 砚山县| 武义县| 平泉县| 施甸县| 建德市| 土默特左旗| 卓尼县|