記一次數據讀取的問題

  • 業務場景是這樣的:

    調用批量修改用戶金幣的方法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來完美解決這個問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章