4. 本地缓存 vs 分布式缓存:Caffeine、Redis、Memcached 选型对比

在微服务与分布式架构中,缓存选型直接影响系统的响应速度、数据一致性以及运维复杂度。本章从服务边界出发,系统对比 Caffeine(本地缓存)、Redis(分布式缓存) 与 Memcached(分布式缓存) 三者的核心差异,并给出基于业务场景的选型决策树。

4.1 缓存分类:本地 vs 分布式

缓存按部署位置分为两类:

  • 本地缓存(进程内缓存):与应用运行在同一 JVM 进程中,如 Caffeine、Guava Cache、Ehcache。访问延迟极低(纳秒级),但数据无法跨节点共享。
  • 分布式缓存(独立中间件):部署为独立服务,应用通过网络访问,如 Redis、Memcached。数据可被所有服务实例共享,但引入网络开销(毫秒级)。
架构原则:本地缓存适合“读多写少、数据量小、一致性要求低”的场景;分布式缓存适合“数据需共享、容量大、一致性要求高”的场景。两者常组合为多级缓存。
本地缓存 vs 分布式缓存架构对比 本地缓存 vs 分布式缓存架构对比 本地缓存(进程内) App 实例 1 App 实例 2 Caffeine 缓存 Caffeine 缓存 ⚠ 数据孤岛:实例间缓存不共享 分布式缓存(独立中间件) App 实例 1 App 实例 2 Redis / Memcached 集群 ✅ 数据共享:所有实例访问同一缓存

4.2 三款缓存核心特性对比

对比维度 Caffeine Redis Memcached
类型 本地进程内缓存 分布式键值存储 分布式键值存储
数据存储 JVM 堆内存 独立进程内存 + 可选持久化 独立进程内存
访问延迟 纳秒级(< 100ns) 毫秒级(0.1-5ms) 毫秒级(0.1-5ms)
数据结构 键值对(Value 为对象) String、List、Set、ZSet、Hash、Stream 等 仅 String(键值对)
持久化 不支持 RDB / AOF 持久化 不支持
过期策略 基于 Window TinyLFU 淘汰 惰性删除 + 定期删除 + 8 种淘汰策略 LRU 淘汰 + 过期时间
集群模式 不支持(单进程) 主从、哨兵、Cluster 分片 客户端分片(无原生集群)
数据一致性 强一致(单进程) 最终一致(主从异步复制) 最终一致(无复制机制)
典型容量 几十 MB ~ 几 GB 几 GB ~ 几百 GB 几 GB ~ 几十 GB
Java 客户端 Caffeine 原生 API Jedis、Lettuce、Redisson SpyMemcached、XMemcached
关键结论:Caffeine 在延迟上碾压分布式缓存,但无法跨进程共享;Redis 在数据结构丰富度和持久化能力上远超 Memcached;Memcached 仅适合极简的纯 KV 缓存场景。

4.3 选型决策树

根据业务需求,按以下步骤决策:

  1. 是否需要跨服务实例共享数据?
    • 否 → 使用 Caffeine(如:单实例配置、用户会话本地副本)
    • 是 → 进入下一步
  2. 是否需要复杂数据结构(List、Set、ZSet、Hash)?
    • 是 → 使用 Redis(如:排行榜、消息队列、计数器)
    • 否 → 进入下一步
  3. 是否需要数据持久化或高可用?
    • 是 → 使用 Redis(如:缓存不可丢失、主从切换)
    • 否 → 使用 Memcached(如:纯临时数据、简单 KV 加速)
避坑提示:不要将 Memcached 用于需要原子操作(如 INCR/DECR 之外的复杂运算)或数据恢复的场景。Redis 的持久化功能会带来额外的写性能开销,在纯缓存场景下建议关闭 AOF 或使用 RDB 策略。

4.4 多级缓存架构:本地 + 分布式组合

在 Spring Boot 实践中,最成熟的方案是 Caffeine(L1) + Redis(L2) 多级缓存:

  • L1(Caffeine):抗住 80% 以上的热点读请求,延迟 < 1μs。
  • L2(Redis):提供数据共享与一致性保障,延迟 1-5ms。
  • 数据库:作为最终数据源,兜底未命中数据。
多级缓存架构(Caffeine + Redis) 多级缓存架构(Caffeine + Redis) 客户端请求 L1: Caffeine 本地缓存 命中 → 直接返回 L2: Redis 分布式缓存 回填 L1 并返回 数据库(兜底)

4.5 Spring Boot 集成示例

以下展示 Caffeine + Redis 双级缓存的配置片段:

// 1. Caffeine 配置(本地缓存)
@Configuration
public class CaffeineConfig {
    @Bean
    public Cache<String, Object> caffeineCache() {
        return Caffeine.newBuilder()
                .maximumSize(10_000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .recordStats()
                .build();
    }
}

// 2. Redis 配置(分布式缓存)
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}

// 3. 多级缓存服务
@Service
public class MultiLevelCacheService {
    @Autowired
    private Cache<String, Object> caffeineCache;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public Object get(String key) {
        // 先查 L1
        Object val = caffeineCache.getIfPresent(key);
        if (val != null) return val;
        // 再查 L2
        val = redisTemplate.opsForValue().get(key);
        if (val != null) {
            caffeineCache.put(key, val); // 回填 L1
            return val;
        }
        // 查数据库(略)
        return null;
    }
}

4.6 选型总结矩阵

业务场景 推荐方案 理由
单实例热点数据(如配置、字典) Caffeine 零网络开销,纳秒级响应
分布式会话 / 用户 Token Redis 需跨实例共享,支持过期与持久化
排行榜 / 计数器 / 消息队列 Redis 原生 ZSet、List、Stream 数据结构
纯 KV 加速,无持久化要求 Memcached 简单高效,内存利用率高
高并发读,容忍短时不一致 Caffeine + Redis 多级缓存,兼顾性能与共享
架构演进建议:初期单实例使用 Caffeine 即可;随着服务拆分,引入 Redis 作为共享缓存层;当 Redis 成为瓶颈时,再考虑在 Redis 前叠加 Caffeine 作为 L1 热点缓存。不要一开始就追求多级缓存,避免过度设计。