引言
原文:
让人头痛的大事务问题到底要如何解决?
读书笔记:担心大佬文章搬家,故整理此学习笔记
大事务可以引发的问题
1. 少用@Transactional注解
- 原因:
- @Transactional注解是通过spring的aop起作用的,使用不当,事务功能可能会失效。
- @Transactional注解一般加在某个业务方法上,会导致整个业务方法都在同一个事务中,粒度太粗,不好控制事务范围,是出现大事务问题的最常见的原因。
- 方案 改用声明式事务
@Transactional
public void add(UserModel userModel) throws Exception {
query1();
query2();
query3();
roleService.save(userModel);
update(userModel);
}
public void save(final User user) {
queryData1();
queryData2();
transactionTemplate.execute((status) => {
addData1();
updateData2();
return Boolean.TRUE;
})
}
2. 将查询(select)方法放到事务外
非事务内必须操作挪出去,典型就是查询操作
3. 事务中避免远程调用
由于网络不稳定,这种远程调的响应时间可能比较长,如果远程调用的代码放在某个事物中,这个事物就可能是大事务。当然,远程调用不仅仅是指调用接口
,还有包括:发MQ消息
,或者连接redis
、mongodb保存数据
等。
远程调用的代码不放在事务中如何保证数据一致性呢?
这就需要建立:重试+补偿机制
,达到数据最终一致性
了。
4. 事务中避免一次性处理太多数据
如果一个事务中需要处理的数据太多,也会造成大事务问题。比如为了操作方便,你可能会一次批量更新1000条数据,这样会导致大量数据锁等待
,特别在高并发的系统中问题尤为明显。
解决办法是分页处理
,1000条数据,分50页,一次只处理20条数据,这样可以大大减少大事务的出现。
5. 非事务执行
业务拆分,非必须放在一个事务里的save方法就不要放到一个事务里;
public void save(final User user) {
transactionTemplate.execute((status) => {
addData();
return Boolean.TRUE;
})
addLog();
updateCount();
}
addLog增加操作日志方法 和 updateCount更新统计数量方法,是可以不在事务中执行的,因为操作日志和统计数量这种业务允许少量数据不一致的情况。
6. 异步处理
@Autowired
private TransactionTemplate transactionTemplate;
...
public void save(final User user) {
transactionTemplate.execute((status) => {
order(); // 下订单
return Boolean.TRUE;
})
sendMq(); // 发货
}
order方法用于下单,delivery方法用于发货,是不是下单后就一定要马上发货呢?
答案是否定的。
这里发货功能其实可以走mq异步处理逻辑。