深入理解dromara/mybatis-jpa-extra的缓存逐出策略:LRU与定时清理
在现代应用开发中,缓存机制是提升系统性能的关键技术之一。dromara/mybatis-jpa-extra作为一款简化MyBatis CRUD操作、增强SELECT分页查询的框架,其缓存策略直接影响着应用的响应速度和资源利用率。本文将深入剖析该框架中基于LRU(最近最少使用)算法和定时清理的双重缓存逐出策略,帮助开发者理解其实现原理与应用场景。
缓存架构概览
dromara/mybatis-jpa-extra的缓存系统采用分层设计,主要包含MyBatis原生缓存和框架扩展缓存两大模块。MyBatis原生缓存通过cacheEnabled配置项控制全局开关,默认开启状态下会缓存所有Mapper配置的查询结果。框架扩展缓存则聚焦于分页查询场景,通过Caffeine缓存库实现高效的SQL语句缓存与自动逐出机制。
核心缓存实现类分布如下:
- MyBatis配置类:mybatis-jpa-extra-spring-boot-starter/src/main/java/org/dromara/mybatis/jpa/starter/MybatisProperties.java
- 分页SQL缓存类:mybatis-jpa-extra/src/main/java/org/dromara/mybatis/jpa/entity/JpaPageSqlCache.java
- 缓存管理类:mybatis-jpa-extra/src/main/java/org/dromara/mybatis/jpa/provider/FetchCountProvider.java
LRU缓存逐出策略
LRU(Least Recently Used)算法是一种基于访问频率的缓存淘汰策略,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也更高"。在dromara/mybatis-jpa-extra中,LRU策略通过Caffeine缓存库实现,主要应用于分页查询SQL的缓存管理。
实现原理
Caffeine缓存库采用了一种结合LRU与W-TinyLFU(Window-Tiny Least Frequently Used)的混合算法,能够在有限的内存空间中保持较高的缓存命中率。框架在FetchCountProvider类中定义了全局分页SQL缓存实例:
public static final Cache<String, JpaPageSqlCache> PAGE_BOUNDSQL_CACHE =
Caffeine.newBuilder()
.expireAfterWrite(300, TimeUnit.SECONDS)
.build();
上述代码创建了一个具有以下特性的缓存实例:
- 键值对结构:以查询ID(String)为键,分页SQL缓存对象(JpaPageSqlCache)为值
- 驱逐策略:当缓存项在指定时间内未被写入时自动过期
- 底层算法:内部采用LRU变种算法管理缓存项优先级
缓存操作流程
分页SQL缓存的典型生命周期包含三个阶段:
缓存获取与失效的核心代码实现如下:
private JpaPageSqlCache getPageSqlCache(String selectId) {
JpaPageSqlCache cache = PAGE_BOUNDSQL_CACHE.getIfPresent(selectId);
PAGE_BOUNDSQL_CACHE.invalidate(selectId);
return cache;
}
这段代码展示了框架的缓存使用模式:每次获取缓存后立即使其失效,确保后续查询能够获取最新数据。这种"即用即弃"的策略虽然牺牲了部分缓存效率,但保证了查询结果的实时性,特别适合数据频繁变动的业务场景。
定时清理机制
除LRU算法外,dromara/mybatis-jpa-extra还实现了基于时间的缓存清理策略。通过设置缓存项的过期时间,框架能够自动清理长时间未使用的缓存数据,避免内存溢出风险。
时间策略配置
在FetchCountProvider类中,缓存构建器通过expireAfterWrite方法设置了300秒(5分钟)的写入后过期时间:
Caffeine.newBuilder()
.expireAfterWrite(300, TimeUnit.SECONDS)
.build();
该配置意味着:当缓存项被创建或更新后,若在接下来的5分钟内没有被再次写入,则会被自动驱逐出缓存。这种策略特别适合以下场景:
- 数据时效性要求高:如电商商品库存、实时统计数据等
- 查询模式稳定:分页查询SQL结构相对固定,缓存命中率高
- 内存资源有限:需要平衡缓存收益与内存占用
定时清理工作流程
定时清理机制的内部工作流程可通过以下状态图表示:
框架通过Caffeine库的定时任务线程实现缓存项的自动过期检查。当缓存项过期时,并不会立即从内存中删除,而是在下次访问或清理线程运行时才会被回收,这种"延迟清理"策略能够减少不必要的性能开销。
缓存策略对比分析
LRU与定时清理两种策略各有侧重,在实际应用中需要根据业务场景灵活选择或组合使用。以下表格对比了两种策略的核心特性:
| 特性 | LRU策略 | 定时清理策略 |
|---|---|---|
| 触发条件 | 缓存满时根据访问频率 | 固定时间后自动触发 |
| 内存占用 | 可预测,受限于缓存大小 | 不可预测,可能突发增长 |
| 实现复杂度 | 较高,需维护访问顺序 | 较低,基于时间戳判断 |
| 适用场景 | 访问模式稳定,热点数据集中 | 数据时效性明确,更新周期固定 |
| 命中率 | 较高,优先保留热点数据 | 中等,可能清理仍有价值的数据 |
| 实现类 | FetchCountProvider.java | FetchCountProvider.java |
在dromara/mybatis-jpa-extra中,两种策略被巧妙地结合使用:Caffeine库内部通过LRU算法管理缓存项的优先级,而框架通过设置expireAfterWrite参数实现定时清理。这种组合策略能够兼顾缓存效率和数据新鲜度,是分页查询场景的理想选择。
实践应用与调优建议
基于对dromara/mybatis-jpa-extra缓存策略的深入理解,我们可以提出以下实践建议,帮助开发者优化缓存使用效果:
缓存参数调优
- 过期时间调整:根据业务数据的更新频率调整
expireAfterWrite参数。对于高频更新数据,可缩短至60-120秒;对于低频更新数据,可延长至10-15分钟。
// 高频更新数据配置
.expireAfterWrite(60, TimeUnit.SECONDS)
// 低频更新数据配置
.expireAfterWrite(900, TimeUnit.SECONDS)
- 缓存容量控制:通过
maximumSize方法限制缓存项数量,防止内存溢出:
Caffeine.newBuilder()
.expireAfterWrite(300, TimeUnit.SECONDS)
.maximumSize(10_000) // 最多缓存10,000条记录
.build();
- 弱引用配置:对于非关键数据,可使用弱引用键或值,允许JVM在内存紧张时回收缓存:
Caffeine.newBuilder()
.weakKeys() // 键使用弱引用
.weakValues() // 值使用弱引用
.expireAfterWrite(300, TimeUnit.SECONDS)
.build();
监控与诊断
为确保缓存策略有效运行,建议结合以下监控手段:
- 缓存命中率统计:通过Caffeine的
stats()方法获取缓存性能指标:
CacheStats stats = PAGE_BOUNDSQL_CACHE.stats();
logger.info("缓存命中率: {}%", stats.hitRate() * 100);
logger.info("平均加载时间: {}ms", stats.averageLoadPenalty() / 1_000_000);
- 自定义Metrics指标:集成Spring Boot Actuator暴露缓存指标:
// 添加缓存指标收集
Metrics.gauge("cache.hit.rate", stats, CacheStats::hitRate);
Metrics.gauge("cache.miss.count", stats, CacheStats::missCount);
- 日志分析:通过框架提供的日志输出分析缓存行为:
logger.trace("Count original SQL :\n{}" , selectSql);
logger.trace("Count SQL LowerCase :\n{}" , countSqlLowerCase);
总结与展望
dromara/mybatis-jpa-extra框架通过LRU算法与定时清理相结合的缓存逐出策略,为分页查询场景提供了高效、可靠的性能优化方案。其核心优势体现在:
- 双重保障机制:LRU算法保证热点数据优先保留,定时清理防止内存泄漏
- 灵活配置选项:通过Caffeine构建器可定制多种缓存参数
- 业务适应性强:"即用即弃"的缓存使用模式适合数据频繁变动场景
未来,该缓存系统仍有以下优化空间:
- 动态调整策略:根据系统负载和数据特性自动调整缓存参数
- 多级缓存架构:引入本地缓存+分布式缓存的混合模式
- 智能预热机制:基于历史查询模式提前加载可能被访问的数据
通过深入理解和合理配置这些缓存策略,开发者可以充分发挥dromara/mybatis-jpa-extra的性能潜力,为应用构建高效、稳定的数据访问层。
官方文档:README.md 缓存实现源码:mybatis-jpa-extra/src/main/java/org/dromara/mybatis/jpa/provider/FetchCountProvider.java 缓存实体类:mybatis-jpa-extra/src/main/java/org/dromara/mybatis/jpa/entity/JpaPageSqlCache.java MyBatis配置类:mybatis-jpa-extra-spring-boot-starter/src/main/java/org/dromara/mybatis/jpa/starter/MybatisProperties.java
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




