平时我们在处理高并发时,会遇到这样一种情况,当同一刻(可能相差毫秒级)同时处理某一个事务时,会导致该事务会同时处理同一个逻辑。
例如:支付宝付款对接时,当支付宝支付成功后由于某种原因,成功通知进行了同时刻的多次推送,而本地代码在进行处理时会同时进入到成功处理订单的逻辑以及其他业务处理,可能会导致其他业务处理处理两次的情况。这里可能有人会说,怎么可能会出现两次呢,我们在进行业务逻辑处理之前肯定会判断该订单是否已经处理过,如果处理了根本不会出现两次处理的情况,但是实际情况是,同一秒(可能间隔毫秒级别)推送3次以上,是有可能绕过这个判断的,即使加了事务,但是在事务在读取该条订单记录时并不会上锁。
例如:支付宝付款对接时,当支付宝支付成功后由于某种原因,成功通知进行了同时刻的多次推送,而本地代码在进行处理时会同时进入到成功处理订单的逻辑以及其他业务处理,可能会导致其他业务处理处理两次的情况。这里可能有人会说,怎么可能会出现两次呢,我们在进行业务逻辑处理之前肯定会判断该订单是否已经处理过,如果处理了根本不会出现两次处理的情况,但是实际情况是,同一秒(可能间隔毫秒级别)推送3次以上,是有可能绕过这个判断的,即使加了事务,但是在事务在读取该条订单记录时并不会上锁。
begin tran--默认的隔离级别 readcommited var order=select(orderid); if(order.status=="未付款"){ return; } else { dosomething //这里可能有非常复杂的业务处理逻辑,可能执行时间很长 update(order,"已付款");//将该订单状态进行更改 } commit tran
现在进程1和进程2同一时刻都进来执行这个方法,但是毫秒级的差别,在执行查询的时候可能进程1和进程2读取到的order都是未付款的状态,因为事务只有在update order时才会加锁,所以就很有可能进程1和进程2都会读取到未付款的订单,导致程序走到else执行业务逻辑处理,这就导致了执行两次的情况。
解决方案:(在SQL下进行测试可行)
SELECT * FROM dbo.order WITH(UPDLOCK) WHERE id=1
就是在查询该条订单时加锁,这样别的进程就无法进行读取,进程2的事务就只能等待进程1的事务执行完成之后才能读取,这就可以成功解决该问题。