设为首页 加入收藏

TOP

为什么要拒绝使用大事务进行处理任务(一)
2017-12-18 12:36:44 】 浏览:1370
Tags:为什么 拒绝 使用 事务 进行 处理 任务

前话: 不要迷恋事务,大事务会拖垮你的用户!

相信很多应用都需要进行一些后台任务的处理,这时候应对的,往往是大批量的数据。比如:对数据进行汇总结算,需要全表扫描,更新; 对用户订单状态进行更新,需要全表扫描,进行更新; 对用户的会员有效期处理,也需要全表扫描,更新!

应对这样的场景,就是定时任务job的职责范畴了。

那么问题来了,这样的场景需要进行事务控制吗? 我觉得这个得看业务需求,比如这个状态不是很重要,那么可以不用进行事务控制。但是更多时候,我们希望是有事务的,因为往往更新不会是单表的。

在spring中,有一个简单的注解,即可以帮忙实现事务的控制。

@Transactional(readOnly = false, rollbackFor = Throwable.class, isolation = Isolation.REPEATABLE_READ)

一个事务搞定!但是问题来了,这里报错了。

java.lang.Exception:
### Error updating database.  Cause: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### Cause: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
; SQL []; Lock wait timeout exceeded; try restarting transaction; nested exception is java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction

究其原因,就是mysql 锁等待超时。这里的等待超时有两种情况,

  1. 是该定时任务后执行,是在等待别的客户端释放锁,而迟迟未得到从而超时。
  2. 是其他客户端在操作时,由于被该定时任务长时间的占有锁,从而导致其等待超时。

当然,更多的可能是第二种。为什么呢? 因为定时任务往往需要处理大量的数据,这时,如果使用了一个最外围的事务,那么相当于整个脚本都是运行在该事务中,只要该脚本还未运行完成,那么事务就不会提交,也就不会释放他占有的锁资源。所以,问题就在这里了。所以,我们得避免进行大事务的形成就很有必要了。

事实上,事务的目的是为了保证数据的原子性,准确性,那么也就是说,只要你需要保证的数据做到了,就可以进行事务提交了。所以,可以将大事务拆小,即保证最小事务的执行即可。如:更新一个用户的会员状态,那么只需要查出相关信息,更改状态,写入相应记录,该事务即可提交。

将大事务拆小后,就可以做到快速释放锁的作用,从而避免了其他客户端的锁等待超时问题了。 

样例: 更新用户的账单状态,步骤为: 查询出所有需要更新的账单数量 >> 定稿job执行开始记录 >> 更新每个订单状态 >> 写入job执行结束标识 >> 完成!

该过程,主要耗时是在对每个用户的账单更新,因此,可以将该处作为事务拆小的依据,具体代码如下:

主事务进行总体控制

// 使用新线程进行具体执行功能,需另起事务控制或接收原有事务
    @Override
    public Integer updateBorrowStatus(JobParamBean jobParamBean) {
        String method = "updateBorrowStatus";
        logger.info("enter {} method, jobParamBean:{}", method, jobParamBean);
        // 更新数据库
        Integer result = null;
        Map<String, Object> cond = new HashMap<>();
        //borrowApplyTimeStart, borrowApplyTimeEnd, borrowStatusList, pageStart, perPageNum
//        cond.put("borrowApplyTimeStart", jobParamBean.getStartTime());
//        cond.put("borrowApplyTimeEnd", jobParamBean.getEndTime());
        cond.put("shouldRepayTimeStart", jobParamBean.getStartTime());
        cond.put("shouldRepayTimeEnd", jobParamBean.getEndTime());

        List<String> borrowStatusList = new ArrayList<>();
        borrowStatusList.add("3");
        cond.put("borrowStatusList", borrowStatusList);

        Integer totalUpdate = borrowMapper.countUsersBorrowListByMap(cond);
        String dubboConsumerIp = jobParamBean.getDubboConsumerIp();
        String myServerIp = "127.0.0.1";
        try {
            myServerIp = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            logger.error("get local server ip error:{}", e);
        }

        Date now = new Date();
        JobExecRecordEntity recordEntity = new JobExecRecordEntity();
        BeanUtils.copyProperties(jobParamBean, recordEntity);
        recordEntity.setJobName("autoUpdateBorrowStatus");
        recordEntity.setExecDateStart(jobParamBean.getEndTime());           // 结束时间为最近时间
        recordEntity.setExecDateEnd(jobParamBean.getStartTime());
        recordEntity.setTotalAffectNum(totalUpdate);
        recordEntity.setReqParams(JSONObject.toJSONStri
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Java String 对 null 对象的容错.. 下一篇重构大型业务型写接口 :并行处理..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目