# MyBatis Plus中怎么将Redis作为二级缓存 ## 一、引言 ### 1.1 MyBatis缓存机制概述 MyBatis作为优秀的ORM框架,其缓存机制分为一级缓存和二级缓存: - **一级缓存**:SqlSession级别,默认开启 - **二级缓存**:Mapper级别,需要手动配置 ### 1.2 为什么需要Redis作为二级缓存 传统MyBatis二级缓存存在以下问题: 1. 单机缓存,无法分布式共享 2. 应用重启后缓存丢失 3. 缓存淘汰策略单一 Redis作为内存数据库具有: - 高性能读写(10万+ QPS) - 丰富的数据结构支持 - 完善的过期机制 - 分布式共享能力 ## 二、环境准备 ### 2.1 必要依赖 ```xml <!-- MyBatis Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency> <!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- 对象序列化 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency>
spring: redis: host: 127.0.0.1 port: 6379 password: database: 0 lettuce: pool: max-active: 8 max-wait: -1ms max-idle: 8 min-idle: 0
继承MyBatis的Cache
接口:
public class RedisCache implements Cache { private final String id; private final RedisTemplate<String, Object> redisTemplate; private static final long EXPIRE_TIME = 30L; public RedisCache(String id) { this.id = id; this.redisTemplate = ApplicationContextHolder.getBean("redisTemplate"); } @Override public String getId() { return this.id; } @Override public void putObject(Object key, Object value) { redisTemplate.opsForValue().set(key.toString(), value, EXPIRE_TIME, TimeUnit.MINUTES); } @Override public Object getObject(Object key) { return redisTemplate.opsForValue().get(key.toString()); } // 其他方法实现... }
@CacheNamespace(implementation = RedisCache.class) public interface UserMapper extends BaseMapper<User> { }
<cache type="com.example.cache.RedisCache"/>
序列化方式 | 优点 | 缺点 |
---|---|---|
JDK序列化 | 兼容性好 | 体积大,可读性差 |
JSON | 可读性好,通用性强 | 性能稍差 |
Protobuf | 性能最优,体积最小 | 需要预定义Schema |
推荐配置:
@Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); // 使用Jackson2JsonRedisSerializer Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class); // 配置ObjectMapper ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(mapper); template.setDefaultSerializer(serializer); return template; }
@Scheduled(fixedRate = 10 * 60 * 1000) public void refreshHotData() { // 1. 获取热点数据ID列表 List<Long> hotIds = getHotDataIds(); // 2. 批量刷新缓存 hotIds.forEach(id -> { String cacheKey = "user:" + id; if(redisTemplate.hasKey(cacheKey)) { User user = userMapper.selectById(id); redisTemplate.opsForValue().set(cacheKey, user); } }); }
请求 -> 本地Caffeine缓存 -> Redis缓存 -> 数据库
实现示例:
public Object getDataWithMultiCache(String key) { // 1. 查询本地缓存 Object value = caffeineCache.getIfPresent(key); if(value != null) return value; // 2. 查询Redis value = redisTemplate.opsForValue().get(key); if(value != null) { caffeineCache.put(key, value); return value; } // 3. 查询数据库 value = queryFromDB(key); if(value != null) { redisTemplate.opsForValue().set(key, value); caffeineCache.put(key, value); } return value; }
推荐格式:业务前缀:实体名:主键
示例:order:user:123
策略 | 适用场景 | 示例 |
---|---|---|
全量缓存 | 数据量小,变化少 | 系统配置表 |
部分缓存 | 热点数据 | 用户基础信息 |
结果缓存 | 复杂查询结果 | 商品分类树 |
// 使用Micrometer监控 @Bean public CacheMetricsContributor cacheMetrics() { return new RedisCacheMetricsContributor(redisTemplate); }
关键监控指标: 1. 缓存命中率 2. 平均响应时间 3. QPS 4. 内存使用率
解决方案:
public User getByIdWithNullCache(Long id) { String key = "user:" + id; User user = redisTemplate.opsForValue().get(key); if(user == null) { // 布隆过滤器检查 if(!bloomFilter.mightContain(id)) { return null; } // 查询数据库 user = userMapper.selectById(id); if(user == null) { // 缓存空值 redisTemplate.opsForValue().set(key, "NULL", 5, TimeUnit.MINUTES); return null; } redisTemplate.opsForValue().set(key, user); } else if("NULL".equals(user)) { return null; } return user; }
解决方案:
// 随机过期时间 private long getRandomExpire() { return EXPIRE_TIME + new Random().nextInt(5); }
采用双删策略:
@Transactional public void updateUser(User user) { // 1. 先删除缓存 redisTemplate.delete("user:" + user.getId()); // 2. 更新数据库 userMapper.updateById(user); // 3. 延迟再次删除 executor.schedule(() -> { redisTemplate.delete("user:" + user.getId()); }, 1, TimeUnit.SECONDS); }
@CacheNamespace(implementation = RedisCache.class) public interface ProductMapper extends BaseMapper<Product> { @Select("SELECT * FROM product WHERE category_id = #{categoryId}") @Options(useCache = true) List<Product> selectByCategory(@Param("categoryId") Long categoryId); }
@Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) .entryTtl(Duration.ofMinutes(30)) .disableCachingNullValues(); return RedisCacheManager.builder(factory) .cacheDefaults(config) .transactionAware() .build(); } }
测试环境: - 100万数据量 - 8核16G服务器
方案 | QPS | 平均响应时间 |
---|---|---|
无缓存 | 1,200 | 45ms |
MyBatis二级缓存 | 15,000 | 3ms |
Redis缓存 | 85,000 | 0.8ms |
通过本文的实践,可以将MyBatis查询性能提升50倍以上,特别适合高并发场景下的数据查询优化。实际应用中需要根据业务特点调整缓存策略和过期时间。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。