使用 Redis 和 Caffeine 构建多级缓存
在分布式系统中,缓存是提升性能、降低数据库压力的关键组件。通过缓存,我们可以快速响应用户请求,避免对数据库的频繁访问。本文将介绍如何使用 Redis 和 Caffeine 构建多级缓存,探索其原理和实现。
一、什么是 Redis 和 Caffeine?
1. Redis
Redis 是一个基于内存的高性能分布式缓存系统。它支持多种数据结构(如字符串、哈希、列表、集合等),拥有强大的持久化和分布式特性。Redis 常用来作为一级缓存,也可以用于存储全局会话、限流等场景。
特点
- 数据存储在内存中,读写速度极快。
 - 分布式支持,能够扩展为高可用集群。
 - 支持丰富的数据结构和 Lua 脚本扩展。
 
2.Caffeine
Caffeine 是 Java 平台上一款高效的本地缓存库,由 Google Guava Cache 的作者开发。它专为高性能设计,常用于应用的本地缓存层。Caffeine 提供丰富的特性,如 LRU、LFU 淘汰策略和异步加载。
特点
- 本地缓存(存储在 JVM 内存中),访问延迟极低。
 - 支持灵活的缓存回收策略(基于时间、大小或自定义规则)。
 - 高并发访问,性能优越。
 
二、为什么要构建多级缓存?
单一的缓存层通常不能满足高性能、高可用的业务需求。例如:
- 仅用 Caffeine: 本地缓存虽然访问速度快,但内存有限,缓存容量受限,且无法在分布式环境中共享数据。
 - 仅用 Redis: Redis 作为远程缓存,存在网络传输延迟,无法与本地缓存的速度媲美。
 
多级缓存通过结合本地缓存(Caffeine)和分布式缓存(Redis)的优点,解决了这些问题。
优势
1. 提升性能: 本地缓存优先命中,减少远程缓存和数据库访问。 2. 减轻 Redis 压力: 避免频繁访问 Redis,大幅降低 Redis 负载。 3. 保证数据一致性: 使用旁路缓存策略等机制,保证缓存与数据库一致性。
三、多级缓存策略设计
旁路缓存策略(Cache-Aside Pattern)是多级缓存的经典模式,主要思路如下:
1.查询数据:
- 先查询 Caffeine(本地缓存);
 - 如果未命中,则查询 Redis(远程缓存);
 - 如果仍未命中,则查询数据库,并将结果分别写入 Redis 和 Caffeine。
 
2.更新数据:
- 数据库更新后,清理 Redis 和 Caffeine 中的缓存数据。
 
3.删除数据:
- 数据删除时,移除 Redis 和 Caffeine 中的相关缓存数据。
 
四、实现过程
1. 使用 Caffeine 实现单级缓存
我们先通过 Caffeine 在 Spring Boot 中实现单级缓存,并通过注解方式进行简单的增删改查缓存管理。
(1)引入依赖
在 pom.xml 文件中添加以下依赖:
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
spring-boot-starter-cache提供了自动配置和一些默认的缓存管理器实现。
(2)配置 Caffeine 缓存
在 application.yml 中添加缓存配置:
spring:
  cache:
    type: caffeine
  caffeine:
    spec: maximumSize=1000,expireAfterWrite=5m
(3)开启缓存注解
在主程序类中添加 @EnableCaching 注解:
@SpringBootApplication
@EnableCaching
public class CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class, args);
    }
}
(4)实现业务逻辑
假设我们有一个用户管理的服务类,包含查询、更新和删除操作:
@Service
public class UserService {
    private final Map<Long, User> database = new HashMap<>();
    @Cacheable(value = "users", key = "#id")
    public User getUserById(Long id) {
        // 模拟从数据库查询
        System.out.println("Fetching user from database...");
        return database.get(id);
    }
    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        // 模拟更新数据库
        System.out.println("Updating user in database...");
        database.put(user.getId(), user);
        return user;
    }
    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
        // 模拟删除数据库记录
        System.out.println("Deleting user from database...");
        database.remove(id);
    }
}
2. 使用 Redis + Caffeine 实现多级缓存
(1)引入依赖
在 pom.xml 文件中添加 Redis 和 Caffeine 依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--common-pool-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
<!--jackson-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>
(2)配置 Redis 和 Caffeine
在 application.yml 中配置 Redis 和 Caffeine:
spring:
  redis:
  host: 127.0.0.1
  port: 6379
  lettuce:
    pool:
      max-active: 8
      max-idle: 8
      min-idle: 0
      max-wait: 100ms
  caffeine:
    spec: maximumSize=1000,expireAfterWrite=5m
(3)实现多级缓存管理
多级缓存需要通过手动管理,无法仅依赖注解。我们可以通过创建缓存工具类实现多级缓存逻辑。
@Component
public class MultiLevelCache {
    private final Cache<String, Object> caffeineCache;
    private final RedisTemplate<String, Object> redisTemplate;
    public MultiLevelCache(RedisTemplate<String, Object> redisTemplate) {
        this.caffeineCache = Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .build();
        this.redisTemplate = redisTemplate;
    }
    public Object get(String key) {
        // 1. 查询本地缓存
        Object value = caffeineCache.getIfPresent(key);
        if (value != null) {
            return value;
        }
        // 2. 查询 Redis 缓存
        value = redisTemplate.opsForValue().get(key);
        if (value != null) {
            // 同步到本地缓存
            caffeineCache.put(key, value);
        }
        return value;
    }
    public void put(String key, Object value) {
        // 更新本地缓存
        caffeineCache.put(key, value);
        // 更新 Redis 缓存
        redisTemplate.opsForValue().set(key, value);
    }
    public void evict(String key) {
        // 清除本地缓存
        caffeineCache.invalidate(key);
        // 清除 Redis 缓存
        redisTemplate.delete(key);
    }
}
(4)整合多级缓存到业务逻辑
修改 UserService,整合多级缓存:
@Service
public class UserService {
    @Autowired
    private final MultiLevelCache multiLevelCache;
    private final Map<Long, User> database = new HashMap<>();
    public User getUserById(Long id) {
        String key = "user:" + id;
        User user = (User) multiLevelCache.get(key);
        if (user == null) {
            System.out.println("Fetching user from database...");
            user = database.get(id);
            if (user != null) {
                multiLevelCache.put(key, user);
            }
        }
        return user;
    }
    public User updateUser(User user) {
        String key = "user:" + user.getId();
        System.out.println("Updating user in database...");
        database.put(user.getId(), user);
        multiLevelCache.put(key, user);
        return user;
    }
    public void deleteUser(Long id) {
        String key = "user:" + id;
        System.out.println("Deleting user from database...");
        database.remove(id);
        multiLevelCache.evict(key);
    }
}
五、总结
通过 Redis 和 Caffeine 构建多级缓存,我们结合了本地缓存的高性能和分布式缓存的高容量特性。在实际应用中,多级缓存策略能够显著提高系统的响应速度,同时降低对数据库和远程缓存的依赖。

