Caffeine Cache 高效本地缓存实战

3 阅读6分钟

适配网点/用户低频数据:同步加载+定时刷新+缓存预热一站式实现

在企业级微服务开发中,网点基础资料、用户信息这类数据具备访问频率高、变更频率低的核心特点。若每次请求都直接查询数据库,会产生大量无效IO,严重降低接口响应速度。本文基于Caffeine Cache(Java 性能最强本地缓存框架) ,实现一套同步加载、定时刷新、服务预热的本地缓存方案,零侵入、高性能解决低频静态数据缓存问题。


一、需求背景

  1. 缓存对象:网点基础资料、用户信息等不常变更的业务基础数据;

  2. 核心目标:减少数据库查询压力,提升接口响应性能;

  3. 功能要求

    1. 服务启动时自动预热缓存,避免首次请求卡顿;
    2. 定时自动刷新缓存,保证数据最终一致性;
    3. 支持缓存淘汰监控,方便问题排查;
    4. 纯内存操作,无第三方中间件依赖。

二、方案选型

1. 为什么选择 Caffeine Cache?

  • 性能极致:读写性能远超 Guava Cache、Ehcache,为 Java 生态本地缓存天花板;
  • 策略丰富:支持过期淘汰、异步刷新、容量限制、淘汰监听等企业级特性;
  • Spring 无缝集成:完美适配 Spring Boot,支持依赖注入、定时任务;
  • 轻量无依赖:纯内存缓存,无需部署额外服务,开箱即用。

2. 本方案核心架构

采用 同步加载 + 被动异步刷新 + 定时主动刷新 + 服务启动预热 组合策略:

  1. 同步加载:缓存未命中时,同步加载数据源并自动缓存;
  2. 异步刷新:访问缓存时自动后台刷新,不阻塞业务请求;
  3. 定时刷新:每小时主动强制刷新所有缓存,解决冷数据不更新问题;
  4. 缓存预热:服务启动完成后提前加载所有核心数据。

三、环境搭建

1. Maven 核心依赖

Spring Boot 项目只需引入两个依赖,无需手动管理版本:

<!-- Spring 缓存 Starter(自动整合Caffeine) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Caffeine 本地缓存核心依赖 -->
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

2. 启动类开启核心注解

在 Spring Boot 启动类添加注解,开启缓存定时任务功能:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching // 开启Spring缓存功能
public class OmsApp {
 public static void main(String[] args) {
 SpringApplication.run(OmsApp.class, args);
 }
}

四、核心代码实现

1. 缓存键枚举管理(统一维护)

通过枚举类统一管理所有缓存 Key,避免硬编码,方便后期扩展维护:

package com.xm.kite.caffeineCache;

import java.util.ArrayList;
import java.util.List;

/**
 * 缓存Key枚举
 * 统一管理网点、用户等本地缓存键值
 */
public enum CacheEnum {

    CACHEDEPTS("CACHEDEPTS", "网点基础资料", ""),
    CACHEUSERS("CACHEUSERS", "用户资料", "");

    // 缓存唯一Key
    private final String key;
    // 缓存名称
    private final String cacheName;
    // 缓存描述
    private final String cacheDesc;

    CacheEnum(String key, String cacheName, String cacheDesc) {
        this.key = key;
        this.cacheName = cacheName;
        this.cacheDesc = cacheDesc;
    }

    public String getKey() {
        return key;
    }

    /**
     * 获取所有缓存Key
     */
    public static List<String> getAllKeys() {
        List<String> keys = new ArrayList<>();
        for (CacheEnum cacheEnum : CacheEnum.values()) {
            keys.add(cacheEnum.getKey());
        }
        return keys;
    }

    /**
     * 根据Key匹配枚举
     */
    public static CacheEnum getByKey(String key) {
        if (key == null) {
            return null;
        }
        for (CacheEnum cacheEnum : CacheEnum.values()) {
            if (cacheEnum.getKey().equals(key)) {
                return cacheEnum;
            }
        }
        return null;
    }
}

2. Caffeine 核心配置类

实现缓存构建、同步加载、定时刷新、缓存预热、淘汰监听全套企业级功能:

package com.xm.kite.caffeineCache;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;

import java.util.concurrent.TimeUnit;

/**
 * Caffeine 本地缓存核心配置
 * 同步加载 + 定时主动刷新 + 服务预热 + 淘汰监控
 */
@Slf4j
@Configuration
public class CaffeineConfig {

    /**
     * 构建LoadingCache同步缓存Bean
     * 支持自动加载、异步刷新、过期淘汰
     */
    @Bean
    @Qualifier("caffeineCache")
    public LoadingCache<String, Object> caffeineCache() {
        return Caffeine.newBuilder()
                // 初始容量:预分配内存,提升性能
                .initialCapacity(100)
                // 最大缓存条数:防止内存溢出
                .maximumSize(200)
                // 兜底过期:写入3小时后强制删除缓存
                .expireAfterWrite(3, TimeUnit.HOURS)
                // 异步刷新:2小时后访问时自动后台刷新(不阻塞)
                .refreshAfterWrite(2, TimeUnit.HOURS)
                // 缓存淘汰监听器:监控缓存删除原因
                .evictionListener((key, value, cause) -> {
                    log.info("【Caffeine缓存淘汰】key={}, 原因={}", key, cause);
                })
                // 同步加载器:缓存未命中/刷新时加载数据源
                .build(this::loadUserFromDatabase);
    }

    /**
     * 注入缓存Bean
     */
    @Resource
    @Qualifier("caffeineCache")
    private LoadingCache<String, Object> caffeineCacheBean;

    /**
     * 定时主动刷新缓存
     * cron:每小时整点执行
     * 作用:解决冷数据不触发refreshAfterWrite的问题
     */
    @Scheduled(cron = "0 0 * * * ?")
    public void autoRefreshCache() {
        log.info("【Caffeine】开始每小时定时刷新缓存");
        CacheEnum.getAllKeys().forEach(caffeineCacheBean::refresh);
        log.info("【Caffeine】定时缓存刷新完成");
    }

    /**
     * 同步加载数据源
     * 实际业务中替换为数据库/RPC查询逻辑
     */
    public Object loadUserFromDatabase(String key) {
        CacheEnum cacheEnum = CacheEnum.getByKey(key);
        if (cacheEnum == null) {
            return "未知缓存数据:" + key;
        }
        // 业务数据加载(可替换为真实查询)
        return switch (cacheEnum) {
            case CACHEDEPTS -> "网点基础资料数据";
            case CACHEUSERS -> "用户信息数据";
        };
    }

    /**
     * 服务启动缓存预热
     * @PostConstruct:Bean初始化完成后执行
     */
    @PostConstruct
    public void warmUpCache() {
        log.info("【Caffeine】服务启动,开始缓存预热");
        CacheEnum.getAllKeys().forEach(key -> {
            Object cacheData = caffeineCacheBean.get(key);
            log.info("【Caffeine】缓存预热成功 key={}, data={}", key, cacheData);
        });
    }
}

五、核心功能详解

1. 同步加载机制

使用 LoadingCache 同步缓存,调用 get(key) 时:

  • 缓存存在 → 直接返回数据;
  • 缓存不存在 → 自动执行 loadUserFromDatabase 加载数据并缓存;
  • 保证数据强一致性,适合本地基础数据缓存。

2. 双缓存刷新策略

  1. 被动异步刷新( refreshAfterWrite
  1. 缓存写入2小时后,访问时后台异步加载新数据,不阻塞业务请求;
  1. 主动定时刷新( @Scheduled
  1. 每小时主动调用 refresh(key) 强制刷新所有缓存,解决冷数据不更新问题。

3. 兜底过期保护

expireAfterWrite(3, TimeUnit.HOURS)

缓存写入3小时后强制删除,防止缓存数据永久不更新,保证数据最终一致性。

4. 缓存淘汰监听

实时监控缓存删除行为,打印淘汰原因:

  • EXPIRED:缓存过期;
  • REPLACED:缓存被覆盖/刷新;
  • SIZE:超出最大容量;
  • EXPLICIT:手动删除。

5. 服务启动预热

通过 @PostConstruct 注解,服务启动完成后自动加载所有缓存数据,彻底避免首次请求卡顿。


六、业务代码使用示例

在 Service 层注入缓存 Bean,直接使用缓存数据:

import com.github.benmanes.caffeine.cache.LoadingCache;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class BaseDataService {

    @Resource
    @Qualifier("caffeineCache")
    private LoadingCache<String, Object> caffeineCache;

    /**
     * 获取网点基础资料(走本地缓存)
     */
    public Object getDeptData() {
        return caffeineCache.get(CacheEnum.CACHEDEPTS.getKey());
    }

    /**
     * 获取用户信息(走本地缓存)
     */
    public Object getUserData() {
        return caffeineCache.get(CacheEnum.CACHEUSERS.getKey());
    }
}

七、方案优势总结

  1. 高性能:纯内存操作,无网络开销,接口响应速度提升 90% 以上;
  2. 高可用:双刷新策略+兜底过期,兼顾性能与数据一致性;
  3. 易维护:枚举统一管理缓存键,日志监控全覆盖;
  4. 零侵入:无需修改业务核心逻辑,配置化实现缓存;
  5. 防雪崩:无全量清空缓存操作,避免数据库压力暴增。

八、适用场景

  • 低频变更、高频访问的基础数据(网点、用户、字典、枚举);
  • 无需分布式共享的单机本地缓存;
  • 追求极致性能、低延迟的企业级接口。

九、总结

本文基于 Caffeine Cache 实现了一套生产级本地缓存解决方案,针对网点、用户信息等低频静态数据,通过同步加载、被动异步刷新、主动定时刷新、服务启动预热四大核心能力,完美解决了数据库查询性能瓶颈。

方案轻量、高效、易落地,是 Spring Boot 项目中本地缓存的最佳实践,可直接复用至物流、电商、ERP 等各类企业业务系统。