核心概念
一级缓存(本地缓存)
- SqlSession 级别缓存
- 默认开启,同一个 SqlSession 内有效
- 执行 DML 操作(增删改)或调用
clearCache()
后失效
二级缓存(全局缓存)
- Mapper(namespace)级别缓存
- 需手动开启,多个 SqlSession 共享
- 跨会话数据共享,适用于读多写少场景
第三方缓存(Redis等)
- 分布式缓存解决方案
- 需集成额外组件(如 mybatis-redis-cache)
操作步骤(详细配置)
1. 开启全局二级缓存
# application.yml
mybatis-plus:
configuration:
cache-enabled: true # 开启二级缓存
2. Mapper 接口启用缓存
@CacheNamespace // 关键注解
public interface UserMapper extends BaseMapper<User> {
// 自定义方法
}
3. 实体类实现序列化
public class User implements Serializable {
private static final long serialVersionUID = 1L;
// 字段...
}
4. XML 文件配置缓存策略
<!-- UserMapper.xml -->
<cache
eviction="FIFO" <!-- 淘汰策略 -->
flushInterval="60000" <!-- 刷新间隔(ms) -->
size="512" <!-- 缓存对象数量 -->
readOnly="true"/> <!-- 是否只读 -->
5. 缓存操作API
// 获取缓存对象
Cache cache = sqlSession.getCache("com.example.mapper.UserMapper");
// 手动操作缓存
cache.putObject("key", user);
cache.getObject("key");
cache.clear(); // 清空缓存
缓存策略详解
策略类型 | 说明 | 适用场景 |
---|---|---|
LRU(默认) | 最近最少使用 | 通用场景 |
FIFO | 先进先出 | 顺序访问数据 |
SOFT | 软引用(GC时回收) | 内存敏感场景 |
WEAK | 弱引用(GC立即回收) | 临时缓存 |
常见错误与解决方案
缓存脏读
- 现象:数据更新后缓存未刷新
- ✅ 解决方案:
<insert flushCache="true"> <!-- 增删改操作自动刷新缓存 -->
@Options(flushCache = Options.FlushCachePolicy.TRUE)
序列化异常
- 现象:
NotSerializableException
- ✅ 解决方案:
- 确认所有实体类实现
Serializable
- 嵌套对象也需序列化
- 检查第三方缓存传输对象
- 确认所有实体类实现
- 现象:
缓存穿透
- 现象:频繁查询不存在的数据
- ✅ 解决方案:
// 空结果缓存 if(user == null) { cache.putObject(key, NULL_OBJECT); }
注意事项
事务隔离性
- 二级缓存可能导致脏读,建议:
- 事务提交后才刷新缓存
- 高并发写场景慎用二级缓存
- 二级缓存可能导致脏读,建议:
缓存一致性
- 多表关联时,更新关联表需清除所有相关缓存
- 使用
@CacheNamespaceRef
声明依赖Mapper
分布式环境
- 单机二级缓存在集群中会不一致
- 必须使用Redis等分布式缓存
使用技巧
细粒度缓存控制
@Select("SELECT * FROM user") @Options(useCache = false) // 禁用本次查询缓存 List<User> selectAllNoCache();
缓存分区管理
@CacheNamespace(implementation=CustomCache.class) public interface UserMapper { @CacheNamespaceRef(RoleMapper.class) // 关联缓存 User selectUserWithRoles(Long id); }
统计缓存命中率
Cache cache = sqlSession.getCache(); System.out.println("命中率: " + cache.getHitRatio()); // 监控缓存效果
最佳实践与性能优化
1. 缓存选择策略
场景 | 推荐方案 |
---|---|
单机简单应用 | 二级缓存+LRU策略 |
集群环境 | Redis分布式缓存 |
高频更新数据 | 禁用缓存或极短过期时间 |
配置类静态数据 | 永久缓存+手动刷新 |
2. Redis集成
添加依赖:
<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-redis</artifactId> <version>1.0.0-beta2</version> </dependency>
创建
redis.properties
:host=localhost port=6379 password= database=0
Mapper配置:
@CacheNamespace(implementation = RedisCache.class) public interface UserMapper extends BaseMapper<User> {}
3. 性能优化技巧
缓存粒度控制
- 大对象拆分为小对象缓存
- 关联对象分开缓存
冷热数据分离
// 热数据缓存 @CacheNamespace(size=1000) public interface HotDataMapper {} // 冷数据缓存 @CacheNamespace(size=100) public interface ColdDataMapper {}
批量操作优化
// 批量插入时清空缓存 int batchSize = 1000; if(users.size() > batchSize) { cache.clear(); // 大批量操作前清空 }
完整示例:Redis缓存配置
// 1. Mapper接口
@CacheNamespace(implementation = RedisCache.class)
public interface ProductMapper extends BaseMapper<Product> {
@Select("SELECT * FROM product WHERE category = #{category}")
List<Product> selectByCategory(String category);
}
// 2. 自定义Redis模板(可选)
public class CustomRedisCache extends RedisCache {
public CustomRedisCache(String id) {
super(id);
// 自定义序列化方式
redisConfig.setSerializer(new Jackson2JsonRedisSerializer<>(Product.class));
}
}
// 3. 缓存手动管理
@Service
public class ProductService {
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
public void refreshProductCache(Long productId) {
Cache cache = sqlSessionTemplate.getCache(ProductMapper.class.getName());
cache.removeObject(productId); // 清除特定缓存
}
}
# application.yml 补充配置
spring:
redis:
host: redis-server
port: 6379
timeout: 3000
黄金实践原则:
- 读多写少:缓存适合查询频率远高于更新频率的数据
- 时效容忍:接受一定时间的数据延迟
- 缓存降级:实现缓存失效时的后备方案
- 监控告警:集成监控系统跟踪缓存命中率(建议保持>85%)
- 定期维护:设置缓存自动清理策略,避免内存泄漏
通过合理配置缓存,可使查询性能提升5-10倍。但需注意:缓存不是银弹,务必结合业务场景进行压力测试!