性能优化注意:在使用@Transaction加@Cacheable时,缓存会延迟操作!

Spring Boot缓存优化:@Transaction与@Cacheable的事务延迟问题
在Spring Boot中,当@Transaction方法内循环调用@Cacheable方法时,缓存操作会在事务成功提交后执行,导致在事务期间无法从Redis获取数据。这个问题可以通过调整业务逻辑或理解TransactionAwareCacheDecorator的工作原理来解决。

在一个@Transaction方法内,循环调用一个数据库读取操作。代码类似如下:
业务处理类中

@Transactional(rollbackFor = Exception.class)
public void proc(List<Info> infoList) {
	//在infoList中,有很多供应商Id,Id存在很多重复的现像
	for(Info info:infoList){
		//对infoList循环,根据供应商Id取相应的供应商信息
		supplierService.getById(info.getSupplierId());
        //这里的读取效率很低,在这个方法签名上加@Cacheable,使用@Cacheable管理Redis缓存
		//其它处理省略...
	}
}

Service类中:

@Cacheable(value = "supplier", cacheManager = "myRedisCacheManager", key = "#id", unless = "#result == null")
public Supplier getById(Long id) {
	log.debug("读数据库,id:{}",id);
	//数据库查询代码省略
}

期望的结果:当一个supplierId被查询后,放入Redis缓存中,以后再次访问这个方法时,将直接从Redis中读取,,不会进入Service内的方法体。

测试发现的问题:

同一个supplierId,在循环中调用加了@Cacheable的方法时,再次访问时并没有走Redis,日志中发现同一个SupplierId在不停的重复访问数据库,但查看Redis内容中,却发现了Supplier信息已经被缓存了。

 

虽然通过改变业务处理逻辑,可以避过这个问题,但是我仍然希望知道产生这种现象的原因。

 

思路:由于是在SpringBoot框架下,使用了Redis, SpringBoot的Cache接口在Redis下的默认实现类是RedisCache。找到这个类,打开它,在其中搜索Put,找到了public void put(Object key, @Nullable Object value)方法,在这个方法下加上断点。

经断点发现:RedisCache中的put方法,是在循环都执行完毕后,才调用的。这就解释了为什么反复调用@Cacheable方法时,不走Redis的原因,是这个时候,Redis中根本就没有缓存返回的内容。

这又是什么原因导致循环完毕后@Cacheable方法才执行Put操作呢?

难道不是在调用@Cacheable方法时同步处理Redis缓存吗?

继续观察,发现在Put方法断点时的方法调用栈中,有一个方法是afterCommit,看到这个方法名,马上就能联想到可能是在事务提交后才执行了put方法。

看来是在加@Transaction的方法内,调用@Cacheable方法时,put操作会在事务提交后才执行的。

又观察断点,发现put方法是通过一个TransactionAwareCacheDecorator的类来调用的。搜索一下这个类名,找到了这个类的Javadoc文档,内容如下:

Cache decorator which synchronizes its put(java.lang.Object, java.lang.Object)evict(java.lang.Object) and clear() operations with Spring-managed transactions (through Spring's TransactionSynchronizationManager, performing the actual cache put/evict/clear operation only in the after-commit phase of a successful transaction. If no transaction is active, put(java.lang.Object, java.lang.Object)evict(java.lang.Object) and clear() operations will be performed immediately, as usual.

Note: Use of immediate operations such as putIfAbsent(java.lang.Object, java.lang.Object) and evictIfPresent(java.lang.Object) cannot be deferred to the after-commit phase of a running transaction. Use these with care in a transactional environment.

英文能力一般,大概的意思就是:
Cache装饰器管理了put,evict,clear这些操作,使这些操作在带有事务的环境下,只有在事务成功提交后才执行。如果没有事务,就会立即执行。
到此,结论就是:在@Transaction方法内,Cache操作只在事务成功提交后执行。
所以如果有在@Transaction方法内循环取数据的操作,要改变思路才能使Cache操作生效。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值