如何避免大事务问题
引言
原文:
让人头痛的大事务问题到底要如何解决?
读书笔记:担心大佬文章搬家,故整理此学习笔记
大事务可以引发的问题
1. 少用@Transactional注解
- 原因:
- @Transactional注解是通过spring的aop起作用的,使用不当,事务功能可能会失效。
- @Transactional注解一般加在某个业务方法上,会导致整个业务方法都在同一个事务中,粒度太粗,不好控制事务范围,是出现大事务问题的最常见的原因。
- 方案 改用声明式事务
@Transactionalpublic 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. 异步处理
@Autowiredprivate TransactionTemplate transactionTemplate;...public void save(final User user) {transactionTemplate.execute((status) => {order(); // 下订单return Boolean.TRUE;})sendMq(); // 发货}
order方法用于下单,delivery方法用于发货,是不是下单后就一定要马上发货呢?
答案是否定的。
这里发货功能其实可以走mq异步处理逻辑。