Spring Boot启用Redis缓存后数据库仍有请求,是否正常?
问题分析与解决方案
现象是否正常?
绝对不正常。正常情况下,@Cacheable注解生效后,首次请求会执行方法体(触发数据库查询)并将结果存入Redis,后续相同请求会直接读取Redis缓存,不会再触发数据库查询。你遇到的矛盾现象(Redis有缓存命中但DB仍被查询)说明缓存逻辑存在隐性问题。
可能的配置错误排查
1. @Cacheable注解位置错误
将@Cacheable加在UserController的方法上是常见误区:
- Spring缓存基于AOP代理实现,若Controller方法内部直接调用MyBatis Mapper或未被代理的Service方法,即使Controller方法被缓存,方法体内部的DB查询逻辑仍会执行(因为缓存只拦截Controller方法的入口,不影响内部调用)。
- 正确做法:将
@Cacheable迁移到业务层(Service)的核心方法上,比如UserService.getAllUsers(),确保缓存拦截的是DB查询的上游入口。
2. 缓存Key生成异常
虽然getAllUsers是无参方法,但仍可能因Key生成策略问题导致缓存无法命中:
- 检查
@Cacheable的key属性是否被错误配置,比如误用了请求上下文参数(如#request),导致每次请求生成不同Key。 - 无参方法默认Key为
{cacheName}::{methodName},可通过redis-cli执行KEYS *查看生成的Key是否符合预期,确认每次请求使用的是同一个Key。
3. 缓存配置冲突
- 若同时启用了MyBatis二级缓存,可能与Spring缓存产生冲突:MyBatis二级缓存会在Mapper层面缓存数据,导致即使Spring缓存命中,MyBatis仍可能触发查询(实际是读取自身二级缓存,但DBeaver可能误判为DB查询)。建议暂时关闭MyBatis二级缓存(在
mybatis-config.xml中设置<setting name="cacheEnabled" value="false"/>)验证。 - 检查RedisCacheManager的配置,确认未设置过短的缓存过期时间,避免缓存频繁失效导致每次都需重新查库。
4. AOP代理失效
- 若Controller类存在内部方法调用(比如
getAllUsers调用同一个类的其他方法),由于Spring AOP的代理机制,内部调用不会触发缓存拦截,导致DB查询逻辑每次都执行。
更有效的缓存验证方法
1. 日志直接验证
在业务方法(如UserService.getAllUsers())中添加日志打印:
public List<User> getAllUsers() { System.out.println("[DEBUG] 执行数据库查询操作"); return userMapper.selectAll(); }
请求接口时观察日志,若仅首次请求打印该日志,说明缓存生效;若每次都打印,说明缓存未真正拦截方法执行。
2. Redis命令精准验证
- 执行
redis-cli KEYS *查看缓存Key是否存在; - 执行
redis-cli GET <缓存Key>查看缓存内容是否与接口返回一致; - 再次请求接口后,执行
redis-cli EXISTS <缓存Key>确认缓存未被重复写入(若缓存命中,不会执行SET操作)。
3. 关闭Redis测试
临时停止Redis服务,若此时每次请求都触发DB查询(日志打印或DBeaver监控到查询),说明之前的Redis缓存确实在生效;若关闭Redis后现象不变,说明缓存逻辑完全未生效。
4. Spring Boot Actuator统计
启用Actuator缓存端点:
- 在
pom.xml/build.gradle中添加Actuator依赖; - 在
application.yml中配置:
management: endpoints: web: exposure: include: caches
访问/actuator/caches端点,查看对应缓存的hits(命中次数)和misses(未命中次数),直观判断缓存生效情况。
内容的提问来源于stack exchange,提问作者Ryan Cargan
相关产品推荐
相关产品推荐

