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

Redis利用邏輯過期解決緩存擊穿問題

 更新時間:2026年06月07日 09:08:38   作者:ShaneD771  
本文主要介紹了Redis利用邏輯過期解決緩存擊穿問題,介紹了兩種解決方案,互斥鎖與邏輯過期,介紹了兩種解決方案:互斥鎖與邏輯過期,并重點闡述了邏輯過期的實現(xiàn)原理與代碼示例,強調(diào)了在高并發(fā)場景下選擇邏輯過期的重要性及二次檢查的必要性

一、 什么是緩存擊穿?

緩存擊穿(Cache Breakdown) 是指一個熱點 Key(比如某次秒殺活動的商品詳情),在某個時間點過期了。恰好在這個時間點,有大量的并發(fā)請求訪問這個 Key。這些請求發(fā)現(xiàn)緩存過期,瞬間全部打到數(shù)據(jù)庫上,就像在防線上鑿穿了一個洞,導致數(shù)據(jù)庫壓力激增甚至宕機。

核心特征:

  1. 高并發(fā):訪問量巨大。
  2. 熱點 Key:大家都在查同一個數(shù)據(jù)。
  3. 瞬間失效:緩存 TTL 到期,數(shù)據(jù)物理消失。

二、 互斥鎖&邏輯過期

面對緩存擊穿,通常有兩種解法:

1. 互斥鎖(Mutex Lock)

  • 思路:誰發(fā)現(xiàn)緩存過期了,誰就去搶一把鎖。搶到鎖的人去查數(shù)據(jù)庫寫緩存,其他人排隊等待。
  • 優(yōu)點:數(shù)據(jù)強一致性(查到的絕對是新的)。
  • 缺點性能較差。所有人都得等那一個線程干完活,如果不巧那個線程掛了或慢了,后面就是災難性的阻塞。

2. 邏輯過期(Logical Expiration)

  • 思路“永不過期”。不在 Redis 層面設置 TTL,而是把過期時間寫在 Value 里面。發(fā)現(xiàn)“邏輯”過期后,先返回舊數(shù)據(jù),然后異步開個線程去后臺更新。
  • 優(yōu)點高可用,性能極佳。用戶永遠不需要等待,拿了數(shù)據(jù)就走。
  • 缺點:數(shù)據(jù)存在短暫的不一致(在重建完成前,用戶看到的是舊數(shù)據(jù))。

三、 邏輯過期的實現(xiàn)原理

我們不使用 Redis 的 setex 來控制生死,而是引入一個包裝類 RedisData,人為地記錄一個 expireTime。

1. 數(shù)據(jù)結構設計

我們需要一個容器來封裝真實的業(yè)務數(shù)據(jù)和邏輯過期時間:

@Data
public class RedisData {
    private LocalDateTime expireTime; // 邏輯過期時間
    private Object data;              // 真實的業(yè)務數(shù)據(jù)(如 Shop 對象)
}

2. 執(zhí)行流程圖解

  1. 查詢緩存:從 Redis 取出數(shù)據(jù)(邏輯過期前提是數(shù)據(jù)必須預熱,如果 Redis 沒數(shù)據(jù),直接返回空或降級)。
  2. 判斷邏輯時間
    • 如果 expireTime > now():數(shù)據(jù)新鮮,直接返回。
    • 如果 expireTime <= now()邏輯已過期
  3. 重建緩存
    • 搶鎖:嘗試獲取互斥鎖。
    • 搶鎖失敗:說明有人在更新了,不要等,直接返回舊數(shù)據(jù)。
    • 搶鎖成功:再次檢查緩存是否已更新(Double Check)。如果沒更新,則開啟獨立線程查庫寫緩存;如果已更新,直接釋放鎖并返回新數(shù)據(jù)。
  4. 返回數(shù)據(jù):無論是否搶到鎖,主線程都直接返回數(shù)據(jù)(舊的或新的)。

四、 代碼實現(xiàn) (Java)

以下是基于 SpringBoot + StringRedisTemplate 的完整實現(xiàn),包含了二次檢查(Double Check)邏輯

1. 緩存預熱

因為 Redis 里沒有 TTL,數(shù)據(jù)不會自己消失。我們需要在活動開始前把數(shù)據(jù)“預熱”進去。

/**
 * 預熱數(shù)據(jù)到 Redis
 * @param id 商品ID
 * @param expireSeconds 邏輯過期時間(秒)
 */
public void saveShop2redis(Long id, Long expireSeconds) {
    // 1. 查詢數(shù)據(jù)庫
    Shop shop = getById(id);
    
    // 2. 封裝成 RedisData
    RedisData redisData = new RedisData();
    redisData.setData(shop);
    // 重點:設置邏輯過期時間 = 當前時間 + 指定秒數(shù) (注意單位是 PlusSeconds)
    redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds));
    
    // 3. 寫入 Redis (不設置 TTL)
    stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(redisData));
}

2. 業(yè)務邏輯 (queryWithLogicalExpire)

// 線程池:用于異步重建緩存
private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

public Shop queryWithLogicalExpire(Long id) {
    String key = RedisConstants.CACHE_SHOP_KEY + id;
    
    // 1. 從 Redis 查詢
    String shopJson = stringRedisTemplate.opsForValue().get(key);
    
    // 2. 如果未命中(未預熱),直接返回 null
    if (StrUtil.isBlank(shopJson)) {
        return null;
    }

    // 3. 反序列化
    RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);
    JSONObject data = (JSONObject) redisData.getData();
    Shop shop = JSONUtil.toBean(data, Shop.class);
    LocalDateTime expireTime = redisData.getExpireTime();

    // 4. 判斷是否過期
    if (expireTime.isAfter(LocalDateTime.now())) {
        // 未過期,直接返回
        return shop;
    }

    // ==========================================================
    // 5. 已過期,需要緩存重建
    // ==========================================================
    
    String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
    // 6. 嘗試獲取互斥鎖
    boolean isLock = tryLock(lockKey);
    
    if (isLock) {
        // 6.1 獲取鎖成功
        
        // 【二次檢查 (Double Check)】
        // 再次查詢 Redis,防止在上一個線程釋放鎖的瞬間,緩存已經(jīng)被更新了
        shopJson = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(shopJson)) {
             RedisData newRedisData = JSONUtil.toBean(shopJson, RedisData.class);
             LocalDateTime newExpireTime = newRedisData.getExpireTime();
             // 如果發(fā)現(xiàn)已經(jīng)被更新(不過期了)
             if (newExpireTime.isAfter(LocalDateTime.now())) {
                 // 釋放鎖,直接返回新數(shù)據(jù),不再開啟線程重建
                 unlock(lockKey);
                 return JSONUtil.toBean((JSONObject) newRedisData.getData(), Shop.class);
             }
        }
        
        // 6.2 確認依然過期,開啟獨立線程重建
        CACHE_REBUILD_EXECUTOR.submit(() -> {
            try {
                // 重建緩存(假設邏輯過期時間 20秒)
                this.saveShop2redis(id, 20L);
            } catch (Exception e) {
                e.printStackTrace(); // 建議使用 log.error
            } finally {
                // 釋放鎖
                unlock(lockKey);
            }
        });
    }

    // 7. 【核心】無論是否搶到鎖,都直接返回舊數(shù)據(jù),絕不等待!
    return shop;
}

// 輔助方法:獲取鎖
private boolean tryLock(String key) {
    Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(flag);
}

// 輔助方法:釋放鎖
private void unlock(String key) {
    stringRedisTemplate.delete(key);
}

五、 總結

1. 為什么選擇邏輯過期?

邏輯過期本質(zhì)上是一種**“妥協(xié)的藝術”**。它犧牲了短暫的數(shù)據(jù)一致性(用戶可能在幾百毫秒內(nèi)看到舊數(shù)據(jù)),換取了系統(tǒng)在極高并發(fā)下的穩(wěn)定性(Redis 永不阻塞,數(shù)據(jù)庫壓力極?。?/p>

2. 為什么要做二次檢查 (Double Check)?

如果不加二次檢查,在高并發(fā)下,線程 B 可能會在線程 A 重建完剛剛釋放鎖的時候搶到鎖。此時線程 B 以為數(shù)據(jù)還過期,會再次開啟線程去查庫。雖然不影響正確性,但浪費了性能。加上二次檢查可以避免不必要的重建。

到此這篇關于Redis利用邏輯過期解決緩存擊穿問題的文章就介紹到這了,更多相關Redis 緩存擊穿內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Redis中管道操作pipeline的實現(xiàn)

    Redis中管道操作pipeline的實現(xiàn)

    RedisPipeline是一種優(yōu)化客戶端與服務器通信的技術,通過批量發(fā)送和接收命令減少網(wǎng)絡往返次數(shù),提高命令執(zhí)行效率,本文就來介紹一下Redis中管道操作pipeline的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • Quarkus集成redis操作Redisson實現(xiàn)數(shù)據(jù)互通

    Quarkus集成redis操作Redisson實現(xiàn)數(shù)據(jù)互通

    這篇文章主要為大家介紹了Quarkus集成redis操作Redisson實現(xiàn)數(shù)據(jù)互通的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-02-02
  • Redis?Cluster部署實踐

    Redis?Cluster部署實踐

    本文詳細介紹了在一臺機器上上上搭建一個3主3從的Redis集群的步驟,包括準備階段、啟動實例、組成集群、故障恢復等內(nèi)容;并提供了一些常用命令和注意事項,幫助;以幫助讀者成功搭建Redis集群
    2026-04-04
  • Redis實現(xiàn)鎖續(xù)期的項目實踐

    Redis實現(xiàn)鎖續(xù)期的項目實踐

    本文介紹了使用Redis實現(xiàn)分布式鎖的續(xù)期,包括使用Lua腳本、Redlock算法和Redisson客戶端等方法,具有一定的參考價值,感興趣的可以了解一下
    2024-12-12
  • springmvc集成使用redis過程

    springmvc集成使用redis過程

    這篇文章主要介紹了springmvc集成使用redis過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Windows環(huán)境下查看、添加、修改redis數(shù)據(jù)庫的密碼兩種方式

    Windows環(huán)境下查看、添加、修改redis數(shù)據(jù)庫的密碼兩種方式

    在Windows系統(tǒng)上設置Redis密碼的過程與Linux系統(tǒng)類似,但需注意幾個關鍵步驟以確保正確配置,這篇文章主要給大家介紹了關于Windows環(huán)境下查看、添加、修改redis數(shù)據(jù)庫的密碼兩種方式,需要的朋友可以參考下
    2024-07-07
  • redis分片集群的部署和使用教程

    redis分片集群的部署和使用教程

    這篇文章介紹了如何使用Docker和腳本在虛擬機上部署Redis集群,并詳細講解了Spring Boot整合Redis集群的方法,包括配置、讀寫分離以及一些須知,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • redis8.0新特性之布谷鳥過濾器(Cuckoo Filter)的使用

    redis8.0新特性之布谷鳥過濾器(Cuckoo Filter)的使用

    布谷鳥過濾器是一種概率數(shù)據(jù)結構,就像布隆過濾器一樣,可以以非常快速且節(jié)省空間的方式檢查元素是否存在于集合中,同時還支持刪除操作,并在某些場景下表現(xiàn)優(yōu)于布隆過濾器,感興趣的可以了解一下
    2025-08-08
  • Redis數(shù)據(jù)持久化方式技術解析

    Redis數(shù)據(jù)持久化方式技術解析

    Redis(Remote Dictionary Server ),即遠程字典服務,是一個開源的使用ANSI C語言編寫、支持網(wǎng)絡、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫,并提供多種語言的API
    2021-09-09
  • 查看Redis內(nèi)存信息的命令

    查看Redis內(nèi)存信息的命令

    Redis 是一個開源、高性能的Key-Value數(shù)據(jù)庫,被廣泛應用在服務器各種場景中。本文介紹幾個查看Redis內(nèi)存信息的命令,包括常用的info memory、info keyspace、bigkeys等。
    2020-09-09

最新評論

玉龙| 囊谦县| 青阳县| 兰考县| 兴业县| 普洱| 汉沽区| 鄂伦春自治旗| 武平县| 奉化市| 庆安县| 昌乐县| 凌源市| 观塘区| 玉林市| 个旧市| 通海县| 团风县| 依安县| 铜梁县| 抚州市| 开原市| 宣汉县| 安庆市| 大宁县| 海阳市| 大化| 顺昌县| 临潭县| 东平县| 仲巴县| 平阳县| 台山市| 澄城县| 焦作市| 什邡市| 霍林郭勒市| 宜川县| 康马县| 聂荣县| 定襄县|