欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 宝鼎售后问题提交 | 后台管理


新闻资讯

MENU

软件开发知识

yyyy-MM-dd HH:mm:ss.S); recordEntity.setJobEndTime(jobEndTi

点击: 次  来源:昆山软开发 时间:2017-12-20

原文出处: 等你回去来

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

相信许多应用都需要举办一些靠山任务的处理惩罚,这时候应对的,往往是大批量的数据。好比:对数据举办汇总结算,需要全表扫描,更新; 对用户订单状态举办更新,需要全表扫描,举办更新; 对用户的会员有效期处理惩罚,也需要全表扫描,更新!

应对这样的场景,就是按时任务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.toJSONString(jobParamBean));
        recordEntity.setJobParams(JSONObject.toJSONString(cond));
        recordEntity.setExecServerIp(myServerIp);
        recordEntity.setReqServerIp(dubboConsumerIp);
        recordEntity.setJobEndTime(DateUtils.convert(now, "yyyy-MM-dd HH:mm:ss.S"));
        Integer recordAffectNum = jobExecRecordMapper.addJobExecRecord(recordEntity);
        Long recordId = recordEntity.getId();

        Integer realUpdateNum = 0;
        if(totalUpdate != null && totalUpdate > 0) {
            Integer pageStart = 0;
            Integer perPageNum = 10;
            String nowDateStr = DateUtils.convert(now, "yyyy-MM-dd");      //当前时间
            for(int i = 0; i < totalUpdate; i += perPageNum) {
                pageStart = i;
                cond.put("pageStart", pageStart);
                cond.put("perPageNum", perPageNum);
                Integer thisAffectNum = platformAssistantBusiness.pieceUpdateBorrowStatus(cond, nowDateStr);      // 利用帮助类举办小事务的拆分
                realUpdateNum += thisAffectNum;
                recordEntity.setCurrentAffectNum(perPageNum);
                recordEntity.setRealAffectNum(thisAffectNum);
                recordEntity.setStatus("1");
                jobExecRecordMapper.updateJobExecRecord(recordEntity);
            }
        }
        recordEntity.setStatus("5");
        recordEntity.setCurrentAffectNum(0);
        recordEntity.setRealAffectNum(0);
        String jobEndTime = DateUtils.convert(new Date(), "yyyy-MM-dd HH:mm:ss.S");
        recordEntity.setJobEndTime(jobEndTime);
        jobExecRecordMapper.updateJobExecRecord(recordEntity);

        result = totalUpdate;
        logger.info("exit {} method, result:{}", method, result);
        return result;
    }