欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis+Caffeine實(shí)現(xiàn)高效兩級(jí)緩存架構(gòu)的詳細(xì)指南

 更新時(shí)間:2025年07月24日 08:58:19   作者:用戶447267193310  
在現(xiàn)代高并發(fā)系統(tǒng)中,緩存是提升系統(tǒng)性能的關(guān)鍵組件之一,本文將介紹如何結(jié)合 Redis 和 Caffeine 構(gòu)建一個(gè)高效的兩級(jí)緩存系統(tǒng),需要的小伙伴可以了解下

引言

在現(xiàn)代高并發(fā)系統(tǒng)中,緩存是提升系統(tǒng)性能的關(guān)鍵組件之一。傳統(tǒng)的單一緩存方案往往難以同時(shí)滿足高性能和高可用性的需求。本文將介紹如何結(jié)合 Redis 和 Caffeine 構(gòu)建一個(gè)高效的兩級(jí)緩存系統(tǒng),并通過三個(gè)版本的演進(jìn)展示如何逐步優(yōu)化代碼結(jié)構(gòu)。

項(xiàng)目源代碼:github地址、gitee地址

兩級(jí)緩存架構(gòu)概述

兩級(jí)緩存通常由本地緩存(如 Caffeine)和分布式緩存(如 Redis)組成:

  • 本地緩存(Caffeine):基于內(nèi)存,訪問速度極快,但容量有限且無法跨進(jìn)程共享
  • 分布式緩存(Redis):可跨進(jìn)程共享,容量更大,但訪問速度相對(duì)較慢

通過結(jié)合兩者優(yōu)勢(shì),我們可以構(gòu)建一個(gè)既快速又具備一致性的緩存系統(tǒng)。

兩級(jí)緩存的優(yōu)勢(shì)

性能優(yōu)勢(shì)

緩存類型平均延遲延遲波動(dòng)范圍
本地緩存0.05-1ms穩(wěn)定
遠(yuǎn)程緩存1-10ms受網(wǎng)絡(luò)影響大
數(shù)據(jù)庫(kù)查詢10-100ms取決于SQL復(fù)雜度

典型案例:某電商平臺(tái)商品詳情頁采用兩級(jí)緩存后:

  • 單純Redis方案:P99響應(yīng)時(shí)間8ms
  • 兩級(jí)緩存方案:P99響應(yīng)時(shí)間降至2ms

本地緩存的延遲是最低的,遠(yuǎn)遠(yuǎn)低于redis等遠(yuǎn)程緩存,而且本地緩存不受網(wǎng)絡(luò)的影響,所以延遲的波動(dòng)范圍也是最穩(wěn)定的。所以,二級(jí)緩存在性能上有極大的優(yōu)勢(shì)。

系統(tǒng)穩(wěn)定性

1.抗流量洪峰能力

假如電商環(huán)境中出現(xiàn)了秒殺場(chǎng)景,或者促銷活動(dòng)。會(huì)有大量的訪問到同一個(gè)商品或者優(yōu)惠券,以下是兩種情景:

純Redis方案,所有請(qǐng)求直達(dá)Redis,容易導(dǎo)致:

  • 連接池耗盡
  • 帶寬被打滿
  • Redis CPU飆升

兩級(jí)緩存方案:

  • 80%以上請(qǐng)求被本地緩存攔截
  • Redis負(fù)載降低5-10倍
  • 系統(tǒng)整體更平穩(wěn)

2.故障容忍度

由于Redis等遠(yuǎn)程緩存需要通過網(wǎng)絡(luò)連接,如果網(wǎng)絡(luò)出現(xiàn)異常,很容易出現(xiàn)訪問不到數(shù)據(jù)的情況。本地緩存則不存在網(wǎng)絡(luò)問題,所以對(duì)故障的容忍度是非常高的。

網(wǎng)絡(luò)分區(qū)場(chǎng)景測(cè)試

模擬 機(jī)房網(wǎng)絡(luò)抖動(dòng)(丟包率30%):

  • 純Redis方案:錯(cuò)誤率飆升到85%
  • 兩級(jí)緩存方案:核心接口仍保持92%成功率

Caffeine簡(jiǎn)介

Caffeine 是一個(gè)高性能的 Java 本地緩存庫(kù),可以理解為 Java 版的"內(nèi)存臨時(shí)儲(chǔ)物柜"。它的核心特點(diǎn)可以用日常生活中的例子來理解:

就像一個(gè)智能的文件柜:

  • 自動(dòng)整理 - 會(huì)自己清理不常用的文件(基于大小或時(shí)間)
  • 快速查找 - 比去檔案室(數(shù)據(jù)庫(kù))找資料快100倍
  • 空間管理 - 只保留最常用的1000份文件(可配置)

技術(shù)特點(diǎn):

基于 Google Guava 緩存改進(jìn)而來

讀寫性能接近 HashMap(O(1)時(shí)間復(fù)雜度)

提供多種淘汰策略:

// 按數(shù)量淘汰(保留最近使用的1000個(gè))
Caffeine.newBuilder().maximumSize(1000)

// 按時(shí)間淘汰(數(shù)據(jù)保存1小時(shí))
Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS)

典型使用場(chǎng)景:

// 創(chuàng)建緩存(相當(dāng)于準(zhǔn)備一個(gè)儲(chǔ)物柜)
Cache<String, User> cache = Caffeine.newBuilder()
    .maximumSize(100)  // 最多存100個(gè)用戶
    .expireAfterWrite(10, TimeUnit.MINUTES) // 10分鐘不用就清理
    .build();

// 存數(shù)據(jù)(往柜子里放東西)
cache.put("user101", new User("張三"));

// 取數(shù)據(jù)(從柜子拿東西)
User user = cache.getIfPresent("user101");

// 取不到時(shí)自動(dòng)加載(柜子沒有就去倉(cāng)庫(kù)找)
User user = cache.get("user101", key -> userDao.getUser(key));

優(yōu)勢(shì)對(duì)比:

  • 比 HashMap:支持自動(dòng)清理和過期
  • 比 Redis:快100倍(無需網(wǎng)絡(luò)IO)
  • 比 Guava Cache:內(nèi)存效率更高,并發(fā)性能更好

注意事項(xiàng):

  • 僅適用于單機(jī)(不同服務(wù)器間的緩存不共享)
  • 適合緩存不易變的數(shù)據(jù)(如系統(tǒng)配置)
  • JVM重啟后數(shù)據(jù)會(huì)丟失(如需持久化需配合Redis)

版本演進(jìn)

版本1:直接侵入Service代碼

在第一個(gè)版本中,我們直接在 Service 層實(shí)現(xiàn)了兩級(jí)緩存邏輯:

@Override
public Order getOrderById(Integer id) {
    String key = CacheConstant.ORDER + id;
    return (Order) orderCache.get(key, k -> {
        // 先查詢 Redis
        Object obj = redisTemplate.opsForValue().get(key);
        if (obj != null) {
            log.info("get data from redis");
            if (obj instanceof Order) {
                return (Order) obj;
            } else {
                log.warn("Unexpected type from Redis, expected Order but got {}", obj.getClass());
            }
        }

        // Redis沒有或類型不匹配則查詢 DB
        log.info("get data from database");
        Order myOrder = orderMapper.getOrderById(id);
        redisTemplate.opsForValue().set(key, myOrder, 120, TimeUnit.SECONDS);
        return myOrder;
    });
}

優(yōu)點(diǎn)

  • 實(shí)現(xiàn)簡(jiǎn)單直接
  • 緩存邏輯清晰可見

缺點(diǎn)

  • 緩存代碼與業(yè)務(wù)代碼高度耦合
  • 難以復(fù)用緩存邏輯
  • 代碼重復(fù)率高

版本2:使用Spring Cache注解

在spring項(xiàng)目中,提供了CacheManager接口和一些注解,允許讓我們通過注解的方式來操作緩存。先來看一下常用的幾個(gè)注解說明:

1.@Cacheable- 緩存查詢

作用:將方法的返回值緩存起來,下次調(diào)用時(shí)直接返回緩存數(shù)據(jù),避免重復(fù)計(jì)算或查詢數(shù)據(jù)庫(kù)。

適用場(chǎng)景

  • 查詢方法(如 getUserByIdfindProduct
  • 計(jì)算結(jié)果穩(wěn)定的方法

示例

@Cacheable(value = "users", key = "#userId")  
public User getUserById(Long userId) {
    // 如果緩存中沒有,才執(zhí)行此方法
    return userRepository.findById(userId).orElse(null);
}

參數(shù)說明

  • value / cacheNames:緩存名稱(如 "users"
  • key:緩存鍵(支持 SpEL 表達(dá)式,如 #userId
  • condition:條件緩存(如 condition = "#userId > 100"
  • unless:排除某些返回值(如 unless = "#result == null"

2.@CachePut- 更新緩存

作用:方法執(zhí)行后,更新緩存(通常用于 insertupdate 操作)。

適用場(chǎng)景

  • 新增或修改數(shù)據(jù)后同步緩存
  • 避免緩存與數(shù)據(jù)庫(kù)不一致

示例

@CachePut(value = "users", key = "#user.id")  
public User updateUser(User user) {
    return userRepository.save(user); // 更新數(shù)據(jù)庫(kù)后,自動(dòng)更新緩存
}

注意

@Cacheable 不同,@CachePut 一定會(huì)執(zhí)行方法,并更新緩存。

3.@CacheEvict- 刪除緩存

作用:方法執(zhí)行后,刪除緩存(適用于 delete 操作)。

適用場(chǎng)景

  • 數(shù)據(jù)刪除后清理緩存
  • 緩存失效策略

示例

@CacheEvict(value = "users", key = "#userId")  
public void deleteUser(Long userId) {
    userRepository.deleteById(userId); // 刪除數(shù)據(jù)庫(kù)數(shù)據(jù)后,自動(dòng)刪除緩存
}

參數(shù)擴(kuò)展

  • allEntries = true:清空整個(gè)緩存(如 @CacheEvict(value = "users", allEntries = true)
  • beforeInvocation = true:在方法執(zhí)行前刪除緩存(避免方法異常導(dǎo)致緩存未清理)

第二個(gè)版本利用了 Spring 的緩存注解來簡(jiǎn)化代碼,如果要使用上面這幾個(gè)注解管理緩存的話,我們就不需要配置V1版本中的那個(gè)類型為Cache的Bean了,而是需要配置spring中的CacheManager的相關(guān)參數(shù),具體參數(shù)的配置和之前一樣。

注意,在改進(jìn)更新操作的時(shí),這里和V1版本的代碼有一點(diǎn)區(qū)別,在之前的更新操作方法中,是沒有返回值的void類型,但是這里需要修改返回值的類型,否則會(huì)緩存一個(gè)空對(duì)象到緩存中對(duì)應(yīng)的key上。當(dāng)下次執(zhí)行查詢操作時(shí),會(huì)直接返回空對(duì)象給調(diào)用方,而不會(huì)執(zhí)行方法中查詢數(shù)據(jù)庫(kù)或Redis的操作。

@Cacheable(value = "order", key = "#id")
@Override
public Order getOrderById(Integer id) {
    String key = CacheConstant.ORDER + id;
    // 先查詢 Redis
    Object obj = redisTemplate.opsForValue().get(key);
    if (obj != null) {
        log.info("get data from redis");
        if (obj instanceof Order) {
            return (Order) obj;
        } else {
            log.warn("Unexpected type from Redis, expected Order but got {}", obj.getClass());
        }
    }

    // Redis沒有或類型不匹配則查詢 DB
    log.info("get data from database");
    Order myOrder = orderMapper.getOrderById(id);
    redisTemplate.opsForValue().set(key, myOrder, 120, TimeUnit.SECONDS);
    return myOrder;
}

@Override
@CachePut(cacheNames = "order",key = "#order.id")
public Order updateOrder(Order order) {
    log.info("update order data");
    orderMapper.updateOrderById(order);
    //修改 Redis
    redisTemplate.opsForValue().set(CacheConstant.ORDER + order.getId(),
                                    order, 120, TimeUnit.SECONDS);

    return order;
}

@Override
@CacheEvict(cacheNames = "order",key = "#id")
public void deleteOrderById(Integer id) {
    log.info("delete order");
    orderMapper.deleteOrderById(id);
    redisTemplate.delete(CacheConstant.ORDER + id);
}

改進(jìn)點(diǎn)

  • 使用 @Cacheable 注解管理 Caffeine 緩存
  • 減少了部分重復(fù)代碼
  • 緩存配置更加集中

遺留問題

  • Redis 操作仍需手動(dòng)編寫
  • 兩級(jí)緩存的同步邏輯仍需在業(yè)務(wù)代碼中處理

版本3:自定義注解+AOP實(shí)現(xiàn)

如果單純只是使用Cache注解進(jìn)行緩存,還是無法把Redis功能實(shí)現(xiàn)從server模塊中剝離出去。如果按照spring對(duì)cache注解的思路,我們可以自定義注解再利用AOP切片操作,把對(duì)應(yīng)的緩存功能切入到service的代碼中,就能實(shí)現(xiàn)二者之間的解耦。

首先,需要定義一個(gè)注解:

/**
 * 雙緩存注解,用于標(biāo)記需要使用雙緩存(通常為本地緩存和遠(yuǎn)程緩存)的方法
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DoubleCache {
    /**
     * 指定緩存的名稱
     * @return 緩存名稱
     */
    String cacheName();

    /**
     * 指定緩存的鍵,支持Spring EL表達(dá)式
     * @return 緩存鍵
     */
    String key(); //支持springEl表達(dá)式

    /**
     * 指定二級(jí)緩存的超時(shí)時(shí)間,單位默認(rèn)根據(jù)實(shí)現(xiàn)確定(通常為秒)
     * 默認(rèn)值為120
     * @return 二級(jí)緩存超時(shí)時(shí)間
     */
    long l2TimeOut() default 120;

    /**
     * 指定緩存類型
     * 默認(rèn)值為 CacheType.FULL
     * @return 緩存類型
     */
    CacheType type() default CacheType.FULL;
}

定義一個(gè)枚舉類型的變量,表示緩存操作的類型:

public enum CacheType {
    FULL,   //存取
    PUT,    //只存
    DELETE  //刪除
}

如果要支持springEL的表達(dá)式,還需要一個(gè)工具類來解析springEI的表達(dá)式:

public class SpelExpressionUtils {

    /**
     * 解析 SpEL 表達(dá)式并替換變量
     * @param elString 表達(dá)式(如 "user.name")
     * @param map 變量鍵值對(duì)
     * @return 解析后的字符串
     */
    public static String parse(String elString, TreeMap<String, Object> map) {
        // 將輸入的表達(dá)式包裝為 SpEL 表達(dá)式格式
        elString = String.format("#{%s}", elString);
        // 創(chuàng)建 SpEL 表達(dá)式解析器
        ExpressionParser parser = new SpelExpressionParser();
        // 創(chuàng)建標(biāo)準(zhǔn)的評(píng)估上下文,用于存儲(chǔ)變量
        EvaluationContext context = new StandardEvaluationContext();
        // 將傳入的變量鍵值對(duì)設(shè)置到評(píng)估上下文中
        map.forEach(context::setVariable);
        // 使用解析器解析表達(dá)式,使用模板解析上下文
        Expression expression = parser.parseExpression(elString, new TemplateParserContext());
        // 在指定上下文中計(jì)算表達(dá)式的值,并將結(jié)果轉(zhuǎn)換為字符串返回
        return expression.getValue(context, String.class);
    }
}

定義切片,在切片操作中來實(shí)現(xiàn)Caffeine和Redis的緩存操作:

@Slf4j
@Component
@Aspect
@AllArgsConstructor
public class CacheAspect {

    private final Cache<String, Object> cache;
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 定義切點(diǎn),匹配使用了 @DoubleCache 注解的方法
     */
    @Pointcut("@annotation(com.example.redis_caffeine.annonation.DoubleCache)")
    public void cacheAspect() {}

    /**
     * 環(huán)繞通知,處理緩存的讀寫、更新和刪除操作
     * 
     * @param point 切入點(diǎn)對(duì)象,包含方法執(zhí)行的相關(guān)信息
     * @return 方法執(zhí)行的返回結(jié)果
     * @throws Throwable 方法執(zhí)行過程中可能拋出的異常
     */
    @Around("cacheAspect()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        try {
            // 獲取方法簽名和方法對(duì)象
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();

            // 解析參數(shù),將參數(shù)名和參數(shù)值存入 TreeMap 中
            String[] paramNames = signature.getParameterNames();
            Object[] args = point.getArgs();
            TreeMap<String, Object> treeMap = new TreeMap<>();
            for (int i = 0; i < paramNames.length; i++) {
                treeMap.put(paramNames[i], args[i]);
            }

            // 獲取方法上的 @DoubleCache 注解
            DoubleCache annotation = method.getAnnotation(DoubleCache.class);
            // 解析 SpEL 表達(dá)式,得到最終的 key 片段
            String elResult = SpelExpressionUtils.parse(annotation.key(), treeMap);
            // 拼接完整的緩存 key
            String realKey = annotation.cacheName() + CacheConstant.ORDER + elResult;

            // 處理強(qiáng)制更新操作
            if (annotation.type() == CacheType.PUT) {
                // 執(zhí)行目標(biāo)方法
                Object object = point.proceed();
                // 將結(jié)果存入 Redis,并設(shè)置過期時(shí)間
                redisTemplate.opsForValue().set(realKey, object, annotation.l2TimeOut(), TimeUnit.SECONDS);
                // 將結(jié)果存入 Caffeine 緩存
                cache.put(realKey, object);
                return object;
            }

            // 處理刪除操作
            if (annotation.type() == CacheType.DELETE) {
                // 從 Redis 中刪除緩存
                redisTemplate.delete(realKey);
                // 從 Caffeine 緩存中刪除緩存
                cache.invalidate(realKey);
                return point.proceed();
            }

            // 優(yōu)先從 Caffeine 緩存中獲取數(shù)據(jù)
            Object caffeineCache = cache.getIfPresent(realKey);
            if (caffeineCache != null) {
                log.info("get data from caffeine");
                return caffeineCache;
            }

            // 其次從 Redis 中獲取數(shù)據(jù)
            Object redisCache = redisTemplate.opsForValue().get(realKey);
            if (redisCache != null) {
                log.info("get data from redis");
                // 將從 Redis 中獲取的數(shù)據(jù)存入 Caffeine 緩存
                cache.put(realKey, redisCache);
                return redisCache;
            }

            // 最后查詢數(shù)據(jù)庫(kù)
            log.info("get data from database");
            Object object = point.proceed();
            if (object != null) {
                // 將數(shù)據(jù)庫(kù)查詢結(jié)果存入 Redis,并設(shè)置過期時(shí)間
                redisTemplate.opsForValue().set(realKey, object, annotation.l2TimeOut(), TimeUnit.SECONDS);
                // 將數(shù)據(jù)庫(kù)查詢結(jié)果存入 Caffeine 緩存
                cache.put(realKey, object);
            }
            return object;
        } catch (Exception e) {
            // 記錄緩存切面處理過程中的錯(cuò)誤
            log.error("Cache aspect error", e);
            throw e;
        }
    }
}

以上操作的主要工作總結(jié)下來是:

  • 定義切點(diǎn):匹配使用 @DoubleCache 注解的方法
  • 參數(shù)解析與鍵生成:提取方法參數(shù),解析 SpEL 表達(dá)式生成緩存鍵
  • 緩存更新策略:PUT 類型執(zhí)行方法后同步更新 Redis 和本地緩存
  • 緩存刪除策略:DELETE 類型先刪除 Redis 和本地緩存,再執(zhí)行方法
  • 多級(jí)查詢策略:優(yōu)先查本地緩存 → Redis → 數(shù)據(jù)庫(kù),查詢結(jié)果寫入兩級(jí)緩存

執(zhí)行操作流程,以查詢操作為例:

攔截被 @DoubleCache 標(biāo)記的目標(biāo)方法

生成緩存鍵 realKey

依次查詢Caffeine → Redis → 數(shù)據(jù)庫(kù)

將數(shù)據(jù)庫(kù)結(jié)果寫入兩級(jí)緩存并返回

若觸發(fā)更新/刪除操作,則同步清理或更新緩存

/**
* 根據(jù)訂單ID獲取訂單信息
* 使用 @DoubleCache 注解,類型為 FULL,會(huì)執(zhí)行完整的緩存操作邏輯
* @param id 訂單ID
* @return 訂單對(duì)象
*/
@Override
@DoubleCache(cacheName = "order", key = "#id",
             type = CacheType.FULL)
public Order getOrderById(Integer id) {
    return orderMapper.getOrderById(id);
}

/**
* 更新訂單信息
* 使用 @DoubleCache 注解,類型為 PUT,會(huì)執(zhí)行緩存更新操作
* @param order 訂單對(duì)象
*/
@Override
@DoubleCache(cacheName = "order", key = "#id",
             type = CacheType.PUT)
public void updateOrder(Order order) {
    orderMapper.updateOrderById(order);
}

/**
* 根據(jù)訂單ID刪除訂單信息
* 使用 @DoubleCache 注解,類型為 DELETE,會(huì)執(zhí)行緩存刪除操作
* @param id 訂單ID
*/
@Override
@DoubleCache(cacheName = "order", key = "#id",
             type = CacheType.DELETE)
public void deleteOrderById(Integer id) {
    orderMapper.deleteOrderById(id);
}

核心注解

  • @DoubleCache:自定義注解,用于標(biāo)記需要兩級(jí)緩存的方法
  • CacheType:枚舉,定義緩存操作類型(FULL, PUT, DELETE)

AOP實(shí)現(xiàn)要點(diǎn)

  • 解析注解參數(shù)
  • 根據(jù)操作類型執(zhí)行不同的緩存邏輯
  • 處理緩存穿透、雪崩等問題
  • 保證兩級(jí)緩存的一致性

優(yōu)勢(shì)

  • 業(yè)務(wù)代碼完全專注于業(yè)務(wù)邏輯
  • 緩存邏輯集中管理,便于維護(hù)
  • 注解配置靈活,可適應(yīng)不同場(chǎng)景
  • 代碼簡(jiǎn)潔,可讀性高

關(guān)鍵配置

Caffeine 配置

@Configuration
@EnableCaching
public class CaffeineConfig {

    //-----------------------------V1------V3-----------------------------------
    @Bean
    public Cache<String, Object> orderCache() {
        return Caffeine.newBuilder()
                .initialCapacity(128)
                .maximumSize(1024)
                .expireAfterWrite(60, TimeUnit.SECONDS)
                .build();
    }

    //-----------------------------V2------------------------------------------
    @Bean
    public CacheManager cacheManager(){
        CaffeineCacheManager cacheManager=new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .initialCapacity(128)
                .maximumSize(1024)
                .expireAfterWrite(60, TimeUnit.SECONDS));
        return cacheManager;
    }
}

Redis 配置

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {

    RedisTemplate<String, Object> template = new RedisTemplate<>();

    template.setConnectionFactory(factory);

    // 創(chuàng)建 ObjectMapper 實(shí)例,用于 JSON 序列化和反序列化
    ObjectMapper objectMapper = new ObjectMapper();
    // 注冊(cè) JavaTimeModule,用于支持 Java 8 日期時(shí)間類型的序列化和反序列化
    objectMapper.registerModule(new JavaTimeModule());
    // 禁用將日期寫成時(shí)間戳的功能
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    // 啟用默認(rèn)類型信息,用于處理多態(tài)類型的序列化和反序列化
    objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(),
                                       ObjectMapper.DefaultTyping.NON_FINAL);

    // 創(chuàng)建 GenericJackson2JsonRedisSerializer 實(shí)例,使用配置好的 ObjectMapper
    GenericJackson2JsonRedisSerializer serializer =
        new GenericJackson2JsonRedisSerializer(objectMapper);

    template.setKeySerializer(new StringRedisSerializer());

    template.setValueSerializer(serializer);

    template.setHashKeySerializer(new StringRedisSerializer());

    template.setHashValueSerializer(serializer);


    template.afterPropertiesSet();
    return template;
}

性能優(yōu)化建議

合理設(shè)置緩存過期時(shí)間

  • 本地緩存過期時(shí)間應(yīng)短于 Redis 緩存
  • 根據(jù)數(shù)據(jù)更新頻率調(diào)整過期策略

緩存穿透防護(hù)

  • 對(duì)空結(jié)果也進(jìn)行緩存
  • 使用布隆過濾器

緩存雪崩防護(hù)

  • 設(shè)置隨機(jī)過期時(shí)間
  • 實(shí)現(xiàn)熔斷機(jī)制

一致性保證

  • 考慮使用消息隊(duì)列同步多節(jié)點(diǎn)本地緩存
  • 對(duì)于關(guān)鍵數(shù)據(jù),可采用"先更新數(shù)據(jù)庫(kù),再刪除緩存"策略

總結(jié)

通過三個(gè)版本的演進(jìn),我們實(shí)現(xiàn)了一個(gè)從強(qiáng)耦合到完全解耦的兩級(jí)緩存系統(tǒng)。最終版本利用自定義注解和 AOP 技術(shù),既保持了代碼的簡(jiǎn)潔性,又提供了強(qiáng)大的緩存功能。這種架構(gòu)特別適合讀多寫少、對(duì)性能要求較高的場(chǎng)景。

在實(shí)際應(yīng)用中,還需要根據(jù)具體業(yè)務(wù)特點(diǎn)調(diào)整緩存策略,并做好監(jiān)控和指標(biāo)收集,以便持續(xù)優(yōu)化緩存效果。Redis + Caffeine 實(shí)現(xiàn)高效的兩級(jí)緩存架構(gòu)

以上就是Redis+Caffeine實(shí)現(xiàn)高效兩級(jí)緩存架構(gòu)的詳細(xì)指南的詳細(xì)內(nèi)容,更多關(guān)于Redis Caffeine兩級(jí)緩存的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 聊聊redis-dump工具安裝問題

    聊聊redis-dump工具安裝問題

    這篇文章主要介紹了redis-dump工具安裝問題,由于安裝redis-dump工具需要使用rvm?和gem工具所以要提前安裝,詳細(xì)的安裝過程本文給大家提到過,需要的朋友可以參考下
    2022-01-01
  • Windows安裝Redis并添加本地自啟動(dòng)服務(wù)的實(shí)例詳解

    Windows安裝Redis并添加本地自啟動(dòng)服務(wù)的實(shí)例詳解

    這篇文章主要介紹了Windows安裝Redis并添加本地自啟動(dòng)服務(wù)的實(shí)例詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • redis學(xué)習(xí)之RDB、AOF與復(fù)制時(shí)對(duì)過期鍵的處理教程

    redis學(xué)習(xí)之RDB、AOF與復(fù)制時(shí)對(duì)過期鍵的處理教程

    這篇文章主要給大家介紹了關(guān)于redis學(xué)習(xí)之RDB、AOF與復(fù)制時(shí)對(duì)過期鍵處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • redis過期key的刪除策略介紹

    redis過期key的刪除策略介紹

    這篇文章主要介紹了redis過期key的刪除策略,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 配置redis的序列化,注入RedisTemplate方式

    配置redis的序列化,注入RedisTemplate方式

    這篇文章主要介紹了配置redis的序列化,注入RedisTemplate方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Redis 實(shí)現(xiàn)隊(duì)列原理的實(shí)例詳解

    Redis 實(shí)現(xiàn)隊(duì)列原理的實(shí)例詳解

    這篇文章主要介紹了Redis 實(shí)現(xiàn)隊(duì)列原理的實(shí)例詳解的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-09-09
  • redis與mongodb的區(qū)別總結(jié)

    redis與mongodb的區(qū)別總結(jié)

    在本篇文章里小編給大家分享的是關(guān)于redis與mongodb的區(qū)別的相關(guān)知識(shí)點(diǎn)內(nèi)容,有需要的朋友們參考下。
    2019-06-06
  • Redis安裝啟動(dòng)及常見數(shù)據(jù)類型

    Redis安裝啟動(dòng)及常見數(shù)據(jù)類型

    這篇文章主要介紹了Redis安裝啟動(dòng)及常見數(shù)據(jù)類型,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • Redis與MySQL數(shù)據(jù)一致性問題的策略模式及解決方案

    Redis與MySQL數(shù)據(jù)一致性問題的策略模式及解決方案

    開發(fā)中,一般會(huì)使用Redis緩存一些常用的熱點(diǎn)數(shù)據(jù)用來減少數(shù)據(jù)庫(kù)IO,提高系統(tǒng)的吞吐量,本文將給大家介紹了Redis與MySQL數(shù)據(jù)一致性問題的策略模式及解決方案,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07
  • Redis swap空間(虛擬內(nèi)存)的使用詳解

    Redis swap空間(虛擬內(nèi)存)的使用詳解

    這篇文章主要介紹了Redis swap空間的使用示例,幫助大家更好的理解和學(xué)習(xí)使用Redis數(shù)據(jù)庫(kù),感興趣的朋友可以了解下
    2021-03-03

最新評(píng)論