Java分布式缓存VS本地缓存用法及说明

 更新时间:2026年04月01日 10:54:04   作者:大手你不懂  
本文介绍了本地缓存和分布式缓存的区别、适用场景及选择方法,本地缓存如ConcurrentHashMap、GuavaCache等,适用于单机场景,追求极致性能;分布式缓存如Redis,适用于高并发、跨服务共享场景,结合使用两者可发挥各自优势,提升系统性能

在Java开发中,缓存技术是提升系统性能的核心手段。无论是新手还是资深开发者,都会面临一个关键问题:什么时候该用本地缓存?什么时候必须用分布式缓存?

本文将用通俗的语言和实际案例,带你理解两者的区别、适用场景以及如何选择。

一、什么是缓存?为什么要用它?

缓存的本质:将频繁访问但获取速度慢的数据(如数据库查询、远程API调用)存储在访问速度快的介质中,减少对慢速数据源的依赖,从而提升系统性能。

常见的缓存类型

  • 本地缓存:数据存储在应用自己的内存中,速度快但容量有限,受JVM的限制。
  • 分布式缓存:数据存储在独立的缓存服务中(如Redis),支持多服务共享,容量和性能可扩展。

二、本地缓存:速度王者,但“独木难支”

本地缓存(Local Cache)是存储在单个应用实例内存中的缓存,比如你的电脑里有一个“本地文件夹”,所有数据都直接存在这个文件夹里。

常见的Java本地缓存框架包括 Caffeine、Guava Cache、Ehcache 等。

1.本地缓存有哪些?各有什么优劣?

(1)ConcurrentHashMap(Java标准库)

优点

  • 线程安全:通过锁分段机制支持高并发访问。
  • 简单易用:无需额外依赖,直接使用。
  • 性能优越:适用于高并发场景的简单缓存。

缺点

  • 缺少高级功能:没有过期策略、最大容量控制、缓存统计。
  • 没有持久化:程序结束后数据丢失。

适用场景

  • 会话缓存、短期数据缓存(如登录态临时存储)。

代码示例

import java.util.concurrent.ConcurrentHashMap;

public class SimpleCache<K, V> {
    private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();

    public V get(K key) {
        return cache.get(key);
    }

    public void put(K key, V value) {
        cache.put(key, value);
    }

    public void remove(K key) {
        cache.remove(key);
    }
}

(2)Guava Cache(Google开源库)

优点

  • 简单易用:API设计简洁,支持自动加载、过期策略(按时间或大小)。
  • 灵活配置:支持LRU(最近最少使用)、LFU(最不经常使用)等淘汰算法。
  • 高效:适用于中等规模缓存需求。

缺点

  • 功能有限:不支持持久化和分布式缓存。
  • 性能略低于Caffeine。

适用场景

  • 读多写少的场景(如静态资源缓存)。

代码示例

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.Cache;

public class GuavaCacheExample {
    public static void main(String[] args) {
        // 创建缓存:最大容量100,写入后1分钟过期
        Cache<String, String> cache = CacheBuilder.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .build();

        cache.put("key1", "value1");
        String value = cache.getIfPresent("key1"); // 获取缓存
        System.out.println("Value: " + value);
    }
}

(3)Caffeine(高性能本地缓存)

优点

  • 性能顶尖:基于W-TinyLFU算法,命中率接近最优值。
  • 支持异步清理:避免阻塞查询请求。
  • 高度可配置:支持权重控制、异步刷新。

缺点

  • 不支持持久化:数据仅存在于内存中。
  • 依赖JDK8及以上版本。

适用场景

  • 高频读取、低一致性要求的场景(如电商商品详情缓存)。

代码示例

import com.github.benmanes.caffeine.cache.Cache;        // Caffeine缓存接口
import com.github.benmanes.caffeine.cache.Caffeine;     // Caffeine缓存构建器
import java.util.concurrent.TimeUnit;                  // 时间单位工具类

public class CaffeineDemo {

    // 初始化Caffeine缓存实例
    // 1. 最大缓存条目数:1000条(超过后自动淘汰旧数据)
    // 2. 数据写入后10分钟过期(自动失效策略)
    private static final Cache<String, Product> cache = Caffeine.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(10, TimeUnit.MINUTES)
        .build();

    /**
     * 获取商品信息方法
     * @param productId 商品ID
     * @return Product 商品对象
     */
    public Product getProduct(String productId) {
        // Caffeine缓存核心API:get(key, mappingFunction)
        // 1. 如果缓存命中,直接返回缓存值
        // 2. 如果缓存未命中,执行mappingFunction加载数据到缓存
        // 3. mappingFunction参数使用Lambda表达式简化代码
        return cache.get(productId, id -> fetchProductFromDB(id));
    }

    /**
     * 模拟从数据库加载数据的方法
     * @param id 数据标识符
     * @return 数据对象
     */
    private Product fetchProductFromDB(String id) {
        // 实际开发中这里会调用数据库查询接口
        // 当前示例返回模拟数据
        return new Product(id, "Product-" + id);
    }
}

// Product类定义(示例)
class Product {
    private String id;
    private String name;

    public Product(String id, String name) {
        this.id = id;
        this.name = name;
    }

    // Getter方法省略
}

(4)Ehcache(多级缓存支持)

优点

  • 堆内+堆外+磁盘缓存:打破JVM内存限制,适合大数据量场景。
  • 支持分布式集群:通过集群策略解决缓存漂移问题。
  • 兼容JCache标准(JSR-107)。

缺点

  • 配置复杂:需管理堆外内存和磁盘存储。
  • 性能略逊于Caffeine。

适用场景

  • 需要持久化的复杂场景(如日志缓存、监控数据)。

代码示例

import org.ehcache.Cache;                               // Ehcache缓存接口
import org.ehcache.CacheManager;                        // 缓存管理器(管理多个缓存)
import org.ehcache.config.builders.CacheConfigurationBuilder;  // 缓存配置构建器
import org.ehcache.config.builders.ResourcePoolsBuilder;       // 资源池配置构建器
import org.ehcache.config.units.MemoryUnit;             // 内存单位枚举类(MB/GB)

public class EhcacheDemo {

    public static void main(String[] args) {
        // === 第一步:配置资源池 ===
        // ResourcePoolsBuilder用于定义缓存使用的存储资源类型和大小
        ResourcePoolsBuilder resourcePools = ResourcePoolsBuilder.newResourcePoolsBuilder()
            .heap(20, MemoryUnit.MB)       // 堆内缓存:20MB(速度最快,受JVM内存限制)
            .offheap(10, MemoryUnit.MB)    // 堆外缓存:10MB(突破JVM内存限制,速度次之)
            .disk(5, MemoryUnit.GB);       // 磁盘缓存:5GB(容量最大,速度最慢,适合冷数据)

        // === 第二步:创建缓存管理器和缓存实例 ===
        // CacheManagerBuilder构建缓存管理器,withCache()添加具体缓存配置
        // build(true) 表示立即构建(实际生产环境建议延迟初始化)
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
            .withCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(
                String.class, String.class, resourcePools))  // 定义缓存名称和键值类型
            .build(true);

        // 从缓存管理器中获取具体缓存实例
        // 泛型参数指定键值类型为<String, String>
        Cache<String, String> cache = cacheManager.getCache("myCache", String.class, String.class);

        // === 第三步:缓存操作 ===
        // 存入数据:key1 -> value1
        cache.put("key1", "value1");

        // 获取数据:自动从堆内->堆外->磁盘按优先级查找
        System.out.println("Value: " + cache.get("key1"));  // 输出:Value: value1
    }
}

资源池配置

heap(20, MB)

  • 作用:分配20MB堆内内存作为缓存存储
  • 特点:速度最快,但受JVM内存限制,适合热点数据
  • 适用场景:高频访问的核心数据(如商品库存)

offheap(10, MB)

  • 作用:分配10MB堆外内存,突破JVM内存限制
  • 特点:速度略慢于堆内,但比磁盘快100倍+
  • 适用场景:次热点数据或需要避免OOM的场景

disk(5, GB)

  • 作用:分配5GB磁盘空间作为冷数据存储
  • 特点:容量最大,但访问速度最慢(毫秒级)
  • 适用场景:低频访问的冷数据(如历史订单)

性能对比

  • 堆内缓存:纳秒级(≈100ns)
  • 堆外缓存:微秒级(≈1μs)
  • 磁盘缓存:毫秒级(≈1ms)

3.本地缓存的核心优势

  • 极低延迟:访问速度接近纳秒级(比分布式缓存快100倍以上)。
  • 零网络开销:数据直接存放在应用进程内存中,无需跨网络请求。
  • 资源占用少:不需要额外部署中间件(如Redis),运维成本低。

2.本地缓存的典型适用场景

  • 高频读取、低一致性要求:如静态资源(页面模板、配置文件)。
  • 数据量小且更新频率低:如商品分类信息、菜单配置。
  • 单体应用或微服务内部缓存:当数据只在单个服务实例内使用时,如用户登录状态的临时缓存。

三、分布式缓存:高可用与共享的“全能选手”

分布式缓存(Distributed Cache)数据存储在独立的、外部的缓存服务集群中,应用通过网络访问。

实现方案:

  • Redis(推荐),Memcached,KeyDB、Dragonfly。

优缺点:

  • 优点:缓存容量可水平扩展,不受单机限制;所有应用实例共享同一份缓存数据,保证了数据的一致性;服务独立,不随应用重启而丢失数据。
  • 缺点:存在网络开销,访问速度低于本地缓存;需要维护独立的缓存集群,增加了系统复杂度和运维成本。

1.典型适用场景

  • 高并发场景:高频读写、数据量大。如秒杀活动、抢购场景(Redis处理10万+/秒并发请求)。
  • 跨服务共享数据:如用户登录态、购物车信息。
  • 需要持久化的数据:多个服务实例需要协同工作,共享状态。Redis支持RDB和AOF持久化,防止数据丢失。

2.实战案例

假设开发一个社交平台,需要缓存“最新评论”以减少数据库压力:

// 使用Redis缓存最新100条评论
String redisKey = "latest_comments";
jedis.lpush(redisKey, newComment); // 添加新评论到列表头部
jedis.ltrim(redisKey, 0, 99);      // 保留最近100条评论

四、本地缓存 vs 分布式缓存:如何选择?

维度本地缓存分布式缓存
数据一致性多实例间不一致,需额外处理天然保证一致性
访问速度纳秒级(最快)毫秒级(受网络影响)
适用场景单体应用、静态数据、低频更新高并发、跨服务共享、复杂业务
容量限制受JVM内存限制可横向扩展,支持TB级数据
运维复杂度低(无需额外部署)高(需独立部署和维护)

五、实战技巧:本地缓存 + 分布式缓存 = 性能翻倍

在实际项目中,本地缓存和分布式缓存可以结合使用,形成“双层缓存架构”:

分级缓存

  • 请求先查本地缓存(Caffeine)。
  • 未命中则查分布式缓存(Redis)。
  • 最后才查询数据库。

缓存预热

  • 应用启动时,从数据库加载热点数据到本地缓存和Redis,减少首次访问延迟。

数据同步

  • 当Redis中的数据更新时,通过Redis的发布订阅功能通知所有服务实例更新本地缓存。

六、总结:选对工具,事半功倍

  • 本地缓存:适合单机场景,追求极致性能,但需容忍数据不一致。
  • 分布式缓存:适合分布式场景,保证数据共享和一致性,但需要权衡性能和成本。
  • 混合使用:通过“本地缓存 + 分布式缓存”架构,既能享受本地缓存的速度优势,又能利用分布式缓存的共享能力。

在实际开发中,根据业务需求选择合适的工具。如果你还在纠结“Redis还是Caffeine”,不妨先问自己:我的数据需要被多少个服务共享?对延迟的要求有多高? 答案会指引你做出正确的选择。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 使用 Spring Boot 内嵌容器 Undertow创建服务器的方法

    使用 Spring Boot 内嵌容器 Undertow创建服务器的方法

    Undertow是一个非常轻量并高性能的web server,它来自 JBoss。支持blocking和non-blocking两种NIO API。接下来通过本文给大家介绍使用Spring Boot 内嵌容器 Undertow创建服务器的方法,感兴趣的朋友一起看看吧
    2017-11-11
  • 通过实例解析synchronized和lock区别

    通过实例解析synchronized和lock区别

    这篇文章主要介绍了通过实例解析synchronized和lock区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • Java构建菜单树的实现示例

    Java构建菜单树的实现示例

    本文主要介绍了Java构建菜单树的实现示例,像一级菜单,二级菜单,三级菜单甚至更多层级的菜单,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • springbootAOP定义切点获取/修改请求参数方式

    springbootAOP定义切点获取/修改请求参数方式

    这篇文章主要介绍了springbootAOP定义切点获取/修改请求参数方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • java中的SpringBoot框架

    java中的SpringBoot框架

    这篇文章主要介绍了java学习之SpringBoot框架,文章基于Java的相关资料展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-04-04
  • Mybatis 传输List的实现代码

    Mybatis 传输List的实现代码

    本文通过实例代码给大家介绍了mybatis传输list的实现代码,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2017-09-09
  • Mybatis plus Dynamic Datasource 动态数据源及使用方式

    Mybatis plus Dynamic Datasource 动态数据源及使用

    dynamic-datasource-spring-boot-starter是一个基于springboot的快速集成多数据源的启动器,它跟mybatis-plus是一个生态圈里的,很容易集成mybatis-plus,本文介绍Mybatis plus Dynamic Datasource 动态数据源的相关知识,感兴趣的朋友一起看看吧
    2025-09-09
  • 实例解析Java中的构造器初始化

    实例解析Java中的构造器初始化

    这篇文章主要通过实例解析Java中的构造器初始化,代码很简单,叙述很明确,需要的朋友可以了解下。
    2017-09-09
  • 关于java 图形验证码的解决方法

    关于java 图形验证码的解决方法

    本篇文章小编为大家介绍,在java中,使用图形验证码的解决方法。需要的朋友参考下
    2013-04-04
  • 浅谈JAVA8给我带了什么——流的概念和收集器

    浅谈JAVA8给我带了什么——流的概念和收集器

    这篇文章主要介绍了JAVA8流的概念和收集器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04

最新评论