记一次数据读取的问题

  • 业务场景是这样的:

    调用批量修改用户金币的方法userService.batchUpdateAccount(userAccountMap);然后马上又查询刚才修改过金币的用户使用userService.getByUserId(1000001).getAccount().getMoney();(用户和金币账号是两张表 ,getByUserId返回用户对象,用户对象里面引用了账号对象,用的是mybatis, 不过这并不是重点。),重点是我1000001这个账号金币初始是0,增加100金币后,然后用getByUserId查询到的还是0, 再重复调用batchUpdateAccount和getByUserId方法得到的金币是100;就这样每次得到的都是上一次的金币量。

  • 查找问题过程:

    • 怀疑过是mybatis一级缓存和二级缓存的问题,项目并没有开二级缓存,一级缓存应该是在一定的时间内查询的sql一致才有;然后我又在sql语句上下了点功夫;在sql语句加了个条件and ${randomStr} = ${randomStr},sql再加上个随机字符串,这样基本上保证不会触发一级缓存;不过问题依然没有解决。
    • 还有怀疑过dubbo消费端调用dubbo提供者,提供者这边把修改的语句延时处理了(但是几乎不可能)
    • 然后就是漫长的看日志, 对比过程,我发现这两条sql执行几乎是同时的(由于隐私问题,我隐藏了重要信息)。
2018-06-21 20:06:28.602 |-DEBUG <com.xx.article.mapper.AccountMapper.updateMoneyByAccount> - ==>  Preparing: UPDATE xxx_account SET money = money + ?, valid_money = valid_money + ?, play_sum = play_sum + ?, paid_money = paid_money + ?, income_money = income_money + ?, break_even_money = break_even_money + ?, update_time = ? WHERE user_id = ? 
    2018-06-21 20:06:28.602 |-DEBUG <com.xx.article.mapper.UserInfoVoMapper.selectByUserId> - ==>  Preparing: SELECT a.*,b.*,c.* FROM xxx_user_info a LEFT JOIN dwc_account b ON a.user_id=b.user_id LEFT JOIN xxx_level_info c ON a.level_id=c.id WHERE a.user_id = ? 
    2018-06-21 20:06:28.750 |-DEBUG <com.xx.article.mapper.UserInfoVoMapper.selectByUserId> - ==> Parameters: 1000001(Integer)
    2018-06-21 20:06:28.757 |-DEBUG <com.xx.article.mapper.AccountMapper.updateMoneyByAccount> - ==> Parameters: 100(Long), 100(Long), 1(Integer), 0(Long), 100(Long), 100(Long), 2018-06-21 20:06:28.211(Timestamp), 1000001(Integer)
    2018-06-21 20:06:28.760 |-DEBUG <com.xx.article.mapper.AccountMapper.updateMoneyByAccount> - <==    Updates: 1
    2018-06-21 20:06:28.760 |-DEBUG <com.alibaba.druid.pool.PreparedStatementPool> - {conn-10010, pstmt-20000} enter cache
    2018-06-21 20:06:28.773 |-DEBUG <com.rw.article.mapper.UserInfoVoMapper.selectByUserId> - <==      Total: 1
    2018-06-21 20:06:28.774 |-DEBUG <com.alibaba.druid.pool.PreparedStatementPool> - {conn-10009, pstmt-20001} enter cache
  • 然后再看了service层的代码
@Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = java.lang.Exception.class)
    @Async
    public void batchUpdateAccount(Map<Integer, Account> accountList) {
        accountList.forEach((k, v) -> accountMapper.updateMoneyByAccount(v));
    }

      @Override
    public UserInfoVo getByUserId(int userId) {
        return userInfoVoMapper.selectByUserId(userId);
    }
  • 发现有个不太熟悉的注解@Async,查了一下发现这个是来做异步的,应该就是它让两条语句同时执行了,所以取到的数据都是上次的。把它去掉后日志可以看到是按先后顺序执行的sql(由于隐私问题,我隐藏了重要信息):
2018-06-21 20:08:44.892 |-DEBUG <com.xx.article.mapper.AccountMapper.updateMoneyByAccount> - ==>  Preparing: UPDATE xxx_account SET money = money + ?, valid_money = valid_money + ?, play_sum = play_sum + ?, paid_money = paid_money + ?, income_money = income_money + ?, break_even_money = break_even_money + ?, update_time = ? WHERE user_id = ? 
    2018-06-21 20:08:45.002 |-DEBUG <com.xx.article.mapper.AccountMapper.updateMoneyByAccount> - ==> Parameters: 100(Long), 100(Long), 1(Integer), 0(Long), 100(Long), 100(Long), 2018-06-21 20:08:44.724(Timestamp), 1000001(Integer)
    2018-06-21 20:08:45.006 |-DEBUG <com.xx.article.mapper.AccountMapper.updateMoneyByAccount> - <==    Updates: 1
    2018-06-21 20:08:45.006 |-DEBUG <com.alibaba.druid.pool.PreparedStatementPool> - {conn-10010, pstmt-20000} enter cache
    2018-06-21 20:08:45.029 |-DEBUG <com.alibaba.dubbo.remoting.transport.DecodeHandler> -  [DUBBO] Decode decodeable message com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation, dubbo version: 2.5.6, current host: 127.0.0.1
    2018-06-21 20:08:45.030 |-DEBUG <com.xx.article.mapper.UserInfoVoMapper.selectByUserId> - ==>  Preparing: SELECT a.*,b.*,c.* FROM xxx_user_info a LEFT JOIN xxx_account b ON a.user_id=b.user_id LEFT JOIN dwc_level_info c ON a.level_id=c.id WHERE a.user_id = ? 
    2018-06-21 20:08:45.034 |-DEBUG <com.xx.article.mapper.UserInfoVoMapper.selectByUserId> - ==> Parameters: 1000001(Integer)
    2018-06-21 20:08:45.056 |-DEBUG <com.xx.article.mapper.UserInfoVoMapper.selectByUserId> - <==      Total: 1
    2018-06-21 20:08:45.057 |-DEBUG <com.alibaba.druid.pool.PreparedStatementPool> - {conn-10010, pstmt-20001} enter cache


  • 最后附上@Async的一点简介:

Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在spring 3.x之后,就已经内置了@Async来完美解决这个问题

posted on 2018-06-24 16:55  愤怒的苹果ext  阅读(34)  评论(0编辑  收藏  举报

导航