一次Mysql并发问题的处理

一次并发问题的解决,记录一下
场景:
实际开发中遇到了这样一个问题:通过第三方的异步通知,来更新用户账户的余额,最初更新的流程是:1.添加流水;2.流水成功后更新账户。更新账户的方法,是常规造作:查询出账户对象,获取原金额,加上新增金额,之后set进对象属性,再调用updateByPrimaryKeySelective进行修改,开始没有什么问题,后期随着业务拓展,有一天发现某用户更新流水存在,但是账户金额未增加,调后台日志发现,同一时间,有两条修改语句对帐户进行了操作,竟然只成功了一条,有生之年终于让我遇到了高并发场景。。
解决步骤
1.首先我想到的是使用缓存来处理,初步思路是:在查询账户对象时,先从缓存中获取是否存在,(1),若存在,锁住当前对象,进行更新操作,成功后将对象从缓存中移除;(2),若不存在,直接执行更新操作,成功后再将该对象放到缓存中;初步代码是这样:

 @Scheduled(cron = "30 01 19 * * ?")
    public void teataaa() {
        String rid="1";

        // 获取ecache缓存池
        Cache rCode = cacheManager.getCache(Common.random_CODE);//获取缓存管理器
        String cacherid = rCode.get(rid, String.class);
        if(StringUtils.isNotBlank(cacherid)){
            System.out.println("主键缓存存在2222");
            synchronized (this){
              int  ret=updateInfo(Integer.parseInt(rid));
                if(ret>0){
                    rCode.evict(rid);
                }
            }
        }else{
            System.out.println("主键缓存不存在22222");
            int con=updateInfo(Integer.parseInt(rid));
            rCode.put(rid,rid);
        }

2.以上代码再复制一份,开启定时同时执行,结果发现,同时进来的两个修改语句都没有被锁住,全部怼到了数据库上,又是只成功修改了一次。
3.改进,在else中也加上了synchronized,测试后是可以达到效果,但是发现代码已变的非常臃肿,缓存也没有用到,而且据查阅,使用同步锁在并发太大的情况下,对系统会有影响。
4.最终改进,不在代码中计算好之后传入,而是将所有的更新条件传入到sql语句中进行操作,主要的代码是这样:

<update id="updateUsrAccInfo" parameterType="map">
		update usr_acc set
		  amount = amount + #{amount},
		  cash_amount = cash_amount + #{cashAmount},
		  lastupdate_time  = #{lastupdateTime},
		  last_amount = #{lastAmount},
		  last_cash_amount = #{lastCashAmount}
		  where account_id = #{accountId}
	</update>

就是将所有要更新的参数通过集合传到sql语句中,让sql来进行计算,最终测试成功,而且代码也简洁了不少,据查阅,原理是这样的:在mysql中,update table set a=a+?属于原子操作,就是说虽然是同时执行,但是第一个update会对该行数据加上排他锁,直到该修改完成,该锁才会被释放,第二个update才会拿到锁,从而对数据进行修改。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值