用户A给用户B转账的问题 (修复第一版方案已知问题)

第一版是有个很严重的而错误,有兴趣的同学可以先推导下哪里可能出了问题。去看第一版点此

为了方便说明问题,这里只说第一版出现的问题和可能的解决方案。

问题1:
在用户A扣减阶段,更新用户余额是表述有问题,不应该使用remain(剩余余额)更新,而是直接扣减即可。应该使用money变量

//用户A扣减阶段

//略过check用户A状态,余额是否充足等等。

begin transaction{//开启事务
	try{
	//更新用户A的余额,实现预扣,同时对A用户的更新操作在此次事务提交前将处于block状态。利用数据库事务排队。
	   int moidfy =  update user set mount=mount+#{money} where  user='A';
	}catch(OutOfRangeException){    //因为没做扣减检查,可能存在扣减为负数的情况,如果有异常,回滚事务。
	   rollback;//回滚事务
	   return;
	}

	if( modify < 1 ){
	   rollback;   //如果更新影响行数为0,回滚更新操作。接口提示客户端重试。
	   return;
	}

	try{
//在库1中插入操作日志或者叫事务日志,记录“谁转给谁,转了多少钱”,用了前一步的流水号作为log表的主键ID。
	   insert into 1.log(id,from,to,money) values(seq_id,'A','B',49.00);
	}catch(DuplicateExcetion){//如果捕获到主键ID的异常,说明该操作已经执行过,回滚事务即可。
	   rollback;//回滚事务
	   return;
	}
	commit;// 如果日志插入成功,提交事务
}


问题2:
在操作日志重放的过程,不能直接跳过操作日志的重放

 

//用户B增加阶段 
//从库2中查询出已经处理的最大ID,这里存在脏读的情况,但是插入是可以避免,见下面注释。 

long maxSeqId = "select * from 2.log where to ='B' and type='转账' " order by id desc 

//从库1中查询出需要处理的操作日志或者事务日志。
List logList = "select * from 1.log where to ='B' and type='转账' and id>maxSeqId "
for( Log log : logList ){
	begin transaction{//开启事务
            //更新库2中用户B的余额。
	   int moidfy =  update user set mount=mount+#{log.money} where user = #{log.to }

	   if( modify < 1 ){
		rollback;   //如果更新影响行数为0,回滚更新操作。接口提示客户端重试。
		return;
	   }

	   try{
    //在库2中插入日志,如上文所说,存在更新冲突的问题,如果冲突说明该日志已经正确处理过,回滚掉更新,跳过这条日志即可。
		insert into 2.log(id,from,to,money) values(log.id,'A','B',49.00);
   	   }catch(DuplicateExcetion){
		rollback;//回滚事务
		continue;//跳过这条日志,处理下一条。
	   }
	   commit;// 如果日志插入成功,提交事务

	}
}

0.00 avg. rating (0% score) - 0 votes

发表评论

电子邮件地址不会被公开。 必填项已用*标注