《并发扣款,如何保证数据的一致性?》,同用户并发扣费时,有一定概率出现数据不一致。可以使用CAS乐观锁的方式保证数据的一致性,不会降低吞吐量,只需要少量的修改。文章发表后不到24小时就收到近200条评论。其中,问得最多的是ABA题,在《并发扣款一致性优化,CAS下ABA问题,这个话题还没聊完!!!》中进行了扩充。其次,大部分题目都是作业题。为什么一定要用select&set写回余额:UPDATEt_yueSETmoney=$new_moneyWHEREuid=$uidANDmoney=$old_money;为什么不能使用直接扣除的方法:UPDATEt_yueSETmoneymoney=money-$diffWHEREuid=$uid;很多人说在并发的情况下,钱会被扣成负数。为了保证余额不被扣成负数,增加一个where条件:UPDATEt_yueSETmoneymoney=money-$diffWHEREuid=$uidANDmoney-$diff>0;其实也不好。推荐使用:UPDATEt_yueSETmoneymoney=money-$diffWHEREuid=$uidANDmoney>$diff;不幸的是,它仍然不起作用。原因是文章《并发扣款,如何保证数据的一致性?》中点赞数最多的评论不是幂等的。画外音:说明大部分同学都能答对作业。在谈幂等性之前,我们先来看另一个测试用例。假设有一个注册新用户的服务接口:boolRegisterUser($uid,$name){//检查uid是否已经存在selectuidfromt_userwhereuid=$uid;//不是新用户,返回失败if(rows>0)returnfalse;else{//将新用户插入到用户表中insertintot_uservalues($uid,$name);//返回成功returntrue;}}有个测试工程师写了这个接口的测试用例:boolTestCase_RegisterUser(){//创建一些假数据longuid=123;Stringname='shenjian';//调用待测接口boolresult=RegisterUser(uid,name);//期望注册成功,并对结果进行断言Assert(result,true);//返回测试结果returnresult;}这是一个好的测试用例吗?这个用例有什么问题?你会发现,在同样的条件下,这个测试用例执行了两次,结果是不同的:第一次执行,第一次创建数据,调用接口,注册成功;第二次执行再次创建同样的数据,调用接口,会注册失败;这不是一个好的测试用例,多次执行的结果是不同的。什么是幂等性?相同条件下,执行相同的请求,得到相同的结果是幂等的。画外音:谷歌一下,解释的比我好,但是意思应该很清楚。如何把上面的测试用例变成“幂等”的测试用例呢?只需添加一行代码:boolTestCase_RegisterUser(){//创建一些假数据longuid=123;Stringname='shenjian';//删除第一个FakeuserDeleteUser(uid);//调用待测接口boolresult=RegisterUser(uid,name);//预期注册成功,并断言结果Assert(result,true);//返回测试结果returnresult;}这样,同样的条件下,不管这个使用多少次执行case,得到的测试结果是一样的。是不是有点幂等的感觉?读取请求通常是幂等的。写请求,视情况而定:insertx,一般来说,不是幂等的,多次插入得到的结果不一定相同deletex,一般来说,是幂等的,多次删除得到的结果还是同一个集合a=x幂等集合a=a-x不是幂等的。。。所以,这样扣除余额:UPDATEt_yueSETmoney=$new_moneyWHEREuid=$uidANDmoney=$old_money;是一个幂等操作。如果这样扣除余额:UPDATEt_yueSETmoneymoney=money-$diffWHEREuid=$uidANDmoney-$diff>0;不是幂等操作。说到这里,可能有朋友要吵架了。测试用例将被重复执行。扣除怎么可能重复执行呢?再试一次。重试是异常处理中非常常用的方法。你在写业务的时候有没有写过这样的代码:result=DoSomething();if(false==result||TIMEOUT){//错误,或者超时,重试result=DoSomething();}returnresult;当然还会有朋友再抬杠的,我再也不会尝试了!!!画外音:嗯,这是合格还是不合格?你可以决定业务代码怎么写,但你不能决定底层框架代码怎么写:站点框架没有自动重试?服务框架有自动重试吗?服务连接池和数据库连接池是否有自动重试?雪崩;dubbo底层默认调用超时重试。这个设计不好;因此,在有重试的架构体系中,幂等性是一个需要考虑的问题。现在应该明白为什么扣充值业务一般都用:select&set,而不和CASplan一起用:setmoney-=Xplan画外音:充完100话费,为什么多了200元?知其然,知其所以然,希望大家有所收获。【本文为专栏作者《58神剑》原创稿件,转载请联系原作者】点此阅读更多该作者好文
