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

封裝Redis工具類實(shí)踐

 更新時(shí)間:2025年12月06日 09:32:35   作者:Super_Leng  
本文介紹了基于StringRedisTemplate封裝的Redis工具類,該工具類提供了多種方法來處理緩存,包括設(shè)置TTL過期時(shí)間、邏輯過期時(shí)間、處理緩存擊穿和穿透問題,在ShopServiceImpl中調(diào)用了這些方法

一、封裝Redis工具類

基于StringRedisTemplate封裝一個(gè)緩存工具類,滿足下列需求:

  • 方法1:將任意Java對(duì)象序列化為json并存儲(chǔ)在string類型的key中,并且可以設(shè)置TTL過期時(shí)間
  • 方法2:將任意Java對(duì)象序列化為json并存儲(chǔ)在string類型的key中,并且可以設(shè)置邏輯過期時(shí)間,用于處理緩存擊穿問題
  • 方法3:根據(jù)指定的key查詢緩存,并反序列化為指定類型,利用緩存空值的方式解決緩存穿透問題
  • 方法4:根據(jù)指定的key查詢緩存,并反序列化為指定類型,需要利用邏輯過期解決緩存擊穿問題
  • 方法5:根據(jù)指定的key查詢緩存,并反序列化為指定類型,需要利用互斥鎖解決緩存擊穿問題

1. 使用構(gòu)造方法注入StringRedisTemplate

@Slf4j
@Component
public class CacheClient {

    private final StringRedisTemplate stringRedisTemplate;

    // 構(gòu)造方法注入StringRedisTemplate
    public CacheClient(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
}

2. 方法1

將任意Java對(duì)象序列化為json并存儲(chǔ)在string類型的key中,并且可以設(shè)置TTL過期時(shí)間

public void set(String key, Object value, Long time, TimeUnit unit) {
    stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);
}

3. 方法2

將任意Java對(duì)象序列化為json并存儲(chǔ)在string類型的key中,并且可以設(shè)置邏輯過期時(shí)間,用于處理緩存擊穿問題

public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {
    // 設(shè)置邏輯過期
    RedisData redisData = new RedisData();
    redisData.setData(value);
    redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
    // 寫入Redis
    stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
}

4. 方法3

根據(jù)指定的key查詢緩存,并反序列化為指定類型,利用緩存空值的方式解決緩存穿透問題

/**
 * @param keyPrefix 業(yè)務(wù)的緩存key前綴,例如:"cache:shop:"
 * @param id
 * @param type 對(duì)象類型,例如:User.class、Shop.class
 * @param dbFallback 傳入查詢數(shù)據(jù)庫的方法,例如:getById(id);
 * @param time
 * @param unit
 * @param <R>   返回值類型
 * @param <ID>  id的類型,String、Integer、Long。。。
 * @return
 */
public <R,ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit){
    String key = keyPrefix + id;
    // 1.從redis查詢商鋪緩存
    String json = stringRedisTemplate.opsForValue().get(key);
    // 2.判斷是否存在
    if (StrUtil.isNotBlank(json)) {
        // 3.存在,直接返回
        return JSONUtil.toBean(json, type);
    }
    // 判斷命中的是否是空值
    if (json != null) {
        // 返回一個(gè)錯(cuò)誤信息
        return null;
    }

    // 4.不存在,根據(jù)id查詢數(shù)據(jù)庫
    R r = dbFallback.apply(id);
    // 5.不存在,返回錯(cuò)誤
    if (r == null) {
        // 將空值寫入redis
        stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
        // 返回錯(cuò)誤信息
        return null;
    }
    // 6.存在,寫入redis
    this.set(key, r, time, unit);
    return r;
}

5. 方法4

根據(jù)指定的key查詢緩存,并反序列化為指定類型,需要利用邏輯過期解決緩存擊穿問題

@Data
public class RedisData {
    // 邏輯過期時(shí)間
    private LocalDateTime expireTime;
    // 緩存預(yù)熱對(duì)象
    private Object data;
}
public <R, ID> R queryWithLogicalExpire(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit) {
     String key = keyPrefix + id;
     // 1.從redis查詢商鋪緩存
     String json = stringRedisTemplate.opsForValue().get(key);
     // 2.判斷是否存在
     if (StrUtil.isBlank(json)) {
         // 3.存在,直接返回
         return null;
     }
     // 4.命中,需要先把json反序列化為對(duì)象
     RedisData redisData = JSONUtil.toBean(json, RedisData.class);
     R r = JSONUtil.toBean((JSONObject) redisData.getData(), type);
     LocalDateTime expireTime = redisData.getExpireTime();
     // 5.判斷是否過期
     if(expireTime.isAfter(LocalDateTime.now())) {
         // 5.1.未過期,直接返回店鋪信息
         return r;
     }
     // 5.2.已過期,需要緩存重建
     // 6.緩存重建
     // 6.1.獲取互斥鎖
     String lockKey = LOCK_SHOP_KEY + id;
     boolean isLock = tryLock(lockKey);
     // 6.2.判斷是否獲取鎖成功
     if (isLock){
         // 6.3.成功,開啟獨(dú)立線程,實(shí)現(xiàn)緩存重建
         CACHE_REBUILD_EXECUTOR.submit(() -> {
             try {
                 // 查詢數(shù)據(jù)庫
                 R newR = dbFallback.apply(id);
                 // 重建緩存
                 this.setWithLogicalExpire(key, newR, time, unit);
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }finally {
                 // 釋放鎖
                 unlock(lockKey);
             }
         });
     }
     // 6.4.返回過期的商鋪信息
     return r;
 }

6. 方法5

根據(jù)指定的key查詢緩存,并反序列化為指定類型,需要利用互斥鎖解決緩存擊穿問題

/**
 * 自定義互斥鎖,利用redis的setnx方法來表示獲取鎖
 * @param key
 * @return
 */
private boolean tryLock(String key) {
    Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", LOCK_SHOP_TTL, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(flag);
}
/**
 * 釋放互斥鎖
 * @param key
 */
private void unlock(String key) {
    stringRedisTemplate.delete(key);
}
public <R, ID> R queryWithMutex(String keyPrefix, ID id, Class<R> type, Function<ID, R> dbFallback, Long time, TimeUnit unit) {
    String key = keyPrefix + id;
    // 1.從redis查詢商鋪緩存
    String shopJson = stringRedisTemplate.opsForValue().get(key);
    // 2.判斷是否存在
    if (StrUtil.isNotBlank(shopJson)) {
        // 3.存在,直接返回
        return JSONUtil.toBean(shopJson, type);
    }
    // 判斷命中的是否是空值
    if (shopJson != null) {
        // 返回一個(gè)錯(cuò)誤信息
        return null;
    }

    // 4.實(shí)現(xiàn)緩存重建
    // 4.1.獲取互斥鎖
    String lockKey = LOCK_SHOP_KEY + id;
    R r = null;
    try {
        boolean isLock = tryLock(lockKey);
        // 4.2.判斷是否獲取成功
        if (!isLock) {
            // 4.3.獲取鎖失敗,休眠并重試
            Thread.sleep(50);
            return queryWithMutex(keyPrefix, id, type, dbFallback, time, unit);
        }
        // 4.4.獲取鎖成功,根據(jù)id查詢數(shù)據(jù)庫
        r = dbFallback.apply(id);
        // 5.不存在,返回錯(cuò)誤
        if (r == null) {
            // 將空值寫入redis
            stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
            // 返回錯(cuò)誤信息
            return null;
        }
        // 6.存在,寫入redis
        this.set(key, r, time, unit);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } finally {
        // 7.釋放鎖
        unlock(lockKey);
    }
    // 8.返回
    return r;
}

二、調(diào)用Redis工具類

  • 在ShopServiceImpl中,調(diào)用Redis工具類
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {

    @Resource
    private CacheClient cacheClient;


    @Override
    public Result queryById(Long id) {
        // 解決緩存穿透
        Shop shop = cacheClient.queryWithPassThrough(CACHE_SHOP_KEY, id, Shop.class, this::getById, CACHE_SHOP_TTL, TimeUnit.MINUTES);

        // 互斥鎖解決緩存擊穿
        // Shop shop = cacheClient.queryWithMutex(CACHE_SHOP_KEY, id, Shop.class, this::getById, CACHE_SHOP_TTL, TimeUnit.MINUTES);

        // 邏輯過期解決緩存擊穿
        // Shop shop = cacheClient.queryWithLogicalExpire(CACHE_SHOP_KEY, id, Shop.class, this::getById, 20L, TimeUnit.SECONDS);

        if (shop == null) {
            return Result.fail("店鋪不存在!");
        }

        // 返回
        return Result.ok(shop);
    }
}

總結(jié)

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

相關(guān)文章

  • Docker + Redis 部署集群的實(shí)現(xiàn)步驟

    Docker + Redis 部署集群的實(shí)現(xiàn)步驟

    本文詳細(xì)介紹了在三臺(tái)服務(wù)器上部署高可用Redis集群的完整流程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-11-11
  • AOP?Redis自定義注解實(shí)現(xiàn)細(xì)粒度接口IP訪問限制

    AOP?Redis自定義注解實(shí)現(xiàn)細(xì)粒度接口IP訪問限制

    這篇文章主要為大家介紹了AOP?Redis自定義注解實(shí)現(xiàn)細(xì)粒度接口IP訪問限制,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • redis如何設(shè)置database個(gè)數(shù)

    redis如何設(shè)置database個(gè)數(shù)

    這篇文章主要介紹了redis如何設(shè)置database個(gè)數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Redis集群的離線安裝步驟及原理詳析

    Redis集群的離線安裝步驟及原理詳析

    這篇文章主要給大家介紹了關(guān)于Redis集群的離線安裝步驟及原理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Redis之緩存擊穿、穿透、雪崩問題及處理

    Redis之緩存擊穿、穿透、雪崩問題及處理

    文章詳細(xì)介紹了緩存擊穿、緩存穿透和緩存雪崩的概念、觸發(fā)條件、典型場(chǎng)景、可能后果及解決策略,強(qiáng)調(diào)了緩存設(shè)計(jì)中需考慮的多個(gè)方面,包括緩存策略、系統(tǒng)一致性、異常處理和優(yōu)化措施,通過合理設(shè)置TTL、使用分布式鎖、預(yù)熱緩存、邏輯過期刷新、布隆過濾器等多種方法
    2026-04-04
  • 深入理解redis分布式鎖和消息隊(duì)列

    深入理解redis分布式鎖和消息隊(duì)列

    本篇文章主要介紹了深入理解redis分布式鎖和消息隊(duì)列,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-07-07
  • Redis實(shí)現(xiàn)UV統(tǒng)計(jì)的示例代碼

    Redis實(shí)現(xiàn)UV統(tǒng)計(jì)的示例代碼

    本文主要介紹了Redis實(shí)現(xiàn)UV統(tǒng)計(jì)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Redis中RedisSearch使用及應(yīng)用場(chǎng)景

    Redis中RedisSearch使用及應(yīng)用場(chǎng)景

    RedisSearch是一個(gè)強(qiáng)大的全文搜索和索引模塊,可以為Redis添加高效的搜索功能,下面就來介紹一下RedisSearch使用及應(yīng)用場(chǎng)景,感興趣的可以了解一下
    2025-05-05
  • redis實(shí)現(xiàn)紅鎖的示例代碼

    redis實(shí)現(xiàn)紅鎖的示例代碼

    在分布式系統(tǒng)中,實(shí)現(xiàn)一個(gè)可靠的鎖機(jī)制是非常重要的,本文主要介紹了redis實(shí)現(xiàn)紅鎖的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-04-04
  • Redis 熱 Key 問題的解決

    Redis 熱 Key 問題的解決

    熱 Key是指在 Redis 中被高頻訪問的某個(gè) Key,其訪問頻率遠(yuǎn)超其他 Key,導(dǎo)致該 Key 所在的 Redis 實(shí)例或節(jié)點(diǎn)成為系統(tǒng)瓶頸,下面就來介紹一下熱key的問題解決,感興趣的可以了解一下
    2026-02-02

最新評(píng)論

吴堡县| 陈巴尔虎旗| 陈巴尔虎旗| 文水县| 平阳县| 剑河县| 信宜市| 新和县| 巨野县| 同仁县| 阳城县| 河西区| 隆林| 永州市| 扶绥县| 南华县| 措勤县| 开远市| 灵武市| 苏尼特左旗| 克什克腾旗| 班戈县| 五指山市| 镶黄旗| 科技| 虎林市| 会东县| 长寿区| 祁东县| 家居| 五常市| 河曲县| 马山县| 太保市| 札达县| 安图县| 台东县| 沽源县| 和龙市| 山丹县| 黄大仙区|