Ehcache 缓存入门
1. 一句话简介
Ehcache 是一个内嵌于应用 JVM 的 Java 本地缓存框架,核心价值是把高频读取又不会频繁变化的数据缓存进内存,从而减少对数据库等底层存储的访问,降低响应延迟与后端压力。Spring Boot 通过 spring-boot-starter-cache 抽象缓存,再配合 net.sf.ehcache:ehcache 依赖为 Ehcache 提供自动配置,只需在启动类加 @EnableCaching,在方法上加 @Cacheable、@CachePut、@CacheEvict 注解即可无侵入地接入缓存。以本模块为例,UserServiceImpl.get 加 @Cacheable(value = "user", key = "#id") 后,首次查询用户后结果即被缓存,后续相同查询直接命中缓存而不进入方法体(日志只打印一次查询);saveOrUpdate 用 @CachePut 写后同步刷新缓存,delete 用 @CacheEvict 移除缓存,形成完整的缓存读写闭环。
2. 什么时候使用
- ✅ 单 JVM 内的读多写少热点数据:如用户信息、系统配置、字典表、商品详情等被频繁查询但很少修改的数据,缓存命中可显著降低数据库压力。本模块的
user缓存即按主键缓存用户实体。 - ✅ 快速上手、低运维成本:Ehcache 随应用捆绑在进程内,无需单独部署服务端,只需一个 jar 依赖加一个
ehcache.xml配置文件即可运行,适合中小型单机应用快速引入缓存。 - ✅ 需要缓存可溢出到磁盘:Ehcache 支持内存不足时把数据溢出(overflow)到磁盘(
overflowToDisk="true"、diskStore指定磁盘目录),内存有限时也可容纳较大体量的数据,本模块的user缓存与默认缓存均开启了磁盘溢出。 - ✅ 以注解方式声明式缓存:通过 Spring Cache 统一抽象 + 注解即可完成缓存读写,代码侵入低,甚至可临时切换具体缓存实现(如从 Ehcache 换到 Redis 仅需改依赖与配置)。
- ✅ 缓存分层的一级缓存(L1):Ehcache 放在应用进程内、访问无网络开销,可作为本地一级缓存配合 Redis 等分布式二级缓存构成多级缓存体系,进一步降低跨网络访问。
- ❌ 多实例部署且需要缓存共享:Ehcache 缓存存在于各 JVM 内、互不相同,多副本部署时会出现每个实例缓存不一致的问题,分布式共享缓存应选 Redis 等独立服务,或仅把 Ehcache 用作本地 L1。
- ❌ 体量极大、远超单机内存上限的缓存:虽然支持溢出到磁盘,但磁盘访问性能远低于内存,超大缓存更适合独立缓存服务并能横向扩展。
- ❌ 需要缓存持久化可靠恢复:默认
diskPersistent="false"表示重启后数据不持久化恢复;如需可靠持久化或数据不丢失,应结合数据库或 Redis 持久化能力。 - ❌ 面向高性能的现代本地缓存需求:Ehcache 2.x(
net.sf.ehcache)已较老旧,若追求极致的单 JVM 本地缓存读取性能,Caffeine 通常更快;Ehcache 2.x 已进入维护期,新项目需评估升级到 Ehcache 3 或直接选 Caffeine。 - ❌ 强一致、敏感的账务类数据:缓存本质是易失、最终一致的加速层,无法替代数据库的事务与持久化保障,需谨慎处理缓存与数据库的一致性。
3. 常见业务场景
用户信息按主键缓存:以用户 ID 为 key 缓存用户对象,查询时先命中缓存再落库。本模块 get 用 @Cacheable(value="user", key="#id") 缓存、saveOrUpdate 用 @CachePut 写时刷新、delete 用 @CacheEvict 删除时清除,完整演示了业务实体缓存的增删改查闭环,是最典型的实体缓存模型。
字典与配置数据缓存:系统常量、省市县、权限菜单等被多接口反复引用的只读配置,读取频率极高而几乎不变化,适合一次性载入 Ehcache,之后所有请求直接命中内存,显著降低数据库查询。
热点数据防穿透:商品详情、文章内容等高频只读数据,缓存命中后直接返回,避免同一热点反复打到数据库。搭配空值缓存或合理过期时间可缓解缓存穿透。
单实例应用的低延迟加速:无分布式共享诉求的独立服务,用 Ehcache 就能以内存访问(几乎零网络/零序列化开销)替代数据库查询,秒级内承担高 QPS 的热点访问。
多级缓存的一级缓存:在 Redis 分布式缓存前叠加 Ehcache 作为进程内 L1,首先命中本地内存,未命中再查 Redis/数据库,既降低数据库压力也降低跨网络访问频率,适合两级缓存架构。
4. 同类技术对比
本模块基于 Ehcache 2.x(net.sf.ehcache)。以下与主流本地/分布式缓存对比:
| 维度 | Ehcache | Caffeine | Redis | Memcached |
|---|---|---|---|---|
| 存储位置 | 嵌于应用 JVM 内 | 嵌于应用 JVM 内 | 独立网络服务(内存) | 独立网络服务(内存) |
| 多实例共享 | 不支持,各 JVM 独立 | 不支持,各 JVM 独立 | 支持,所有实例共享 | 支持,可多实例共享 |
| 数据结构 | 仅 Key-Value | 仅 Key-Value | String、Hash、List、Set、ZSet、GEO | 仅 Key-Value |
| 持久化/磁盘 | 支持内存溢出到磁盘 | 无(纯内存) | 支持 RDB 快照 + AOF 日志 | 无(纯内存) |
| 网络开销 | 无,直接内存访问 | 无,极快 | 需走网络,单次约 0.1~1ms | 需走网络,性能近于 Redis |
| 读写性能 | 快 | 更快,业界标杆 | 快(有网络延迟) | 快 |
| 配置与运维 | 低,随应用部署 | 低,上手最快 | 中高,需部署维护独立服务 | 中,需部署独立服务 |
| 生态活跃度 | 2.x 维护期,3 由 Caffeine 团队接续 | 活跃、现代 | 活跃、生态丰富 | 成熟但发展趋缓 |
| 适用规模 | 单机应用、读多写少 | 单 JVM 高性能本地缓存 | 分布式系统、大数据量 | 分布式 Key-Value 缓存 |
选型建议:单机应用、需要最快速上手的本地缓存,可继续用 Ehcache,尤其需要缓存溢出到磁盘时它是独特优势;但若只追求极致的单 JVM 本地缓存读取性能,且无磁盘溢出需求,优先选 Caffeine(更快、更现代、Spring Boot 默认缓存候选之一)。多实例部署、需要缓存共享或丰富数据结构时,应选 Redis,它是分布式缓存的事实标准。仅需纯 Key-Value 分布式缓存的简单场景可选 Memcached,但它在数据结构和持久化上弱于 Redis。生产环境多实例系统通常采用多级缓存:Ehcache/Caffeine 作为进程内 L1,Redis 作为跨实例共享的 L2,兼顾延迟与一致性。