Spring性能优化:使用私有字段缓存DB数据避免重复查询是否正确
该写法存在明显问题,不建议直接使用
你的核心诉求是减少重复数据库查询,但当前的实现既没达到性能优化的目的,还会引入严重的生产隐患。
当前代码的两个核心问题
- 没有真正减少数据库请求:你在
process()方法的第一行就执行records = recordDao.getAll(),意味着每次调用process()都会完整查一次数据库,把结果存在实例字段里只是为了供本次调用链上的内部方法使用,完全没有减少DB请求次数,只是省了方法传参的步骤。 - 存在严重线程安全风险:Spring中
@Component注解的类默认是单例模式,整个应用生命周期内只会创建一个SomeClass实例,所有并发请求都会共享这个实例的字段。当多个请求同时调用process()时,A请求刚给records赋完值,还没执行完后续逻辑,B请求就可能覆盖records的值,导致A请求的method()、method2()读到B请求查出来的数据,出现极难排查的偶发业务错误。
对应场景的正确实现方式
场景1:仅需在单次process调用的内部方法间共享查询结果
这种场景根本不需要用实例字段存值,直接在process()方法内定义局部变量存储查询结果,再作为参数传给内部方法即可。局部变量是线程栈封闭的,每个请求的调用栈会持有自己的变量副本,完全不会有并发问题,示例代码如下:
@Component public class SomeClass { private final RecordDao recordDao; public SomeClass(RecordDao recordDao) { this.recordDao = recordDao; } public void process(List<String> data) { // 局部变量,线程安全,每个调用独立持有 List<String> records = recordDao.getAll(); method(records); method2(records); } private void method(List<String> records) { for (Object o : /* 你的外层循环逻辑 */) { for (String s : records) { // 业务处理 } } } private void method2(List<String> records) { for (String s : records) { // 业务处理 } } }
场景2:需要跨请求缓存数据,真正减少数据库查询
如果records是更新频率极低的字典、配置类数据,可以做跨请求缓存,注意不要在业务方法里随意覆盖缓存值:
- 在Bean初始化阶段就完成数据查询加载,避免每次业务请求查库
- 缓存值建议用不可变集合包装,避免运行时被意外修改
- 用
volatile修饰缓存字段保证多线程下的可见性,如果需要支持缓存刷新,要加锁保证刷新过程的线程安全
示例代码如下:
@Component public class SomeClass { private final RecordDao recordDao; // volatile保证多线程可见性,用不可变集合包装避免被意外篡改 private volatile List<String> cachedRecords; public SomeClass(RecordDao recordDao) { this.recordDao = recordDao; // Bean初始化时仅查一次库 this.cachedRecords = Collections.unmodifiableList(recordDao.getAll()); } // 单独提供缓存刷新方法,加锁保证并发安全 public synchronized void refreshCache() { this.cachedRecords = Collections.unmodifiableList(recordDao.getAll()); } public void process(List<String> data) { // 直接使用缓存,无需查库 method(cachedRecords); method2(cachedRecords); } private void method(List<String> records) { // 业务逻辑同上 } private void method2(List<String> records) { // 业务逻辑同上 } }
如果缓存的数据更新频繁,或者有较高的一致性要求,不建议自己手写缓存实现,直接使用成熟的缓存框架(比如Spring Cache)配置过期时间、一致性策略即可,避免自己实现出现漏洞。
内容的提问来源于stack exchange,提问作者Bishop
相关产品推荐
相关产品推荐

