在高并发的分布式环境中,数据的查询和修改很容易导致一致性问题。本文将分享一个非常简单但有效的优化方法。1、业务场景业务场景是采购商品过程中需要查询和修改余额。大致的业务流程如下:(1)从数据库查询用户现有余额SELECTmoneyFROMt_yueWHEREuid=$uid,不妨查询$old_money=100元(2)业务层实现业务逻辑,比如买一个80元的商品有10折if($old_money>80*0.9)$new_money=$old_money-80*0.9=28(3)修改数据库中的余额UPDAtEt_yueSETmoney=$new_moneyWHEREuid=$uid在低并发的情况下,这个过程是没有问题的。原价100元,买了80元(72元)的10折产品,剩余28元。2.潜在问题在分布式环境下,如果并发量很大,这种“查询+修改”的业务很容易出现数据不一致的情况。在限制下,可能会出现这样的异常流程:(1)业务1和业务2同时查询余额,是100元(2)业务1和业务2进行逻辑计算,计算出各自业务的余额,假设业务1计算的余额为28元,业务2计算的余额为38元(3)业务1首先修改数据库中的余额,设置为28元。业务2修改数据库中的余额,设置为38元。这时,异常出现了。原金额100元,从业务1中扣除72元,从业务2中扣除62元,得到剩余的38元。3.问题原因在高并发环境下,数据一致性问题是由于并发读取相同数据(双方读出余额为100)并发写入(一个写回28,另一个写回)引起的38).4、原因分析业务回写1:原始金额为100,为初始状态,回写金额为28。理论上,只有原始金额为100时,才允许回写成功。这一步没有问题。业务2回写:原始金额为100,为初始状态,回写金额为38。理论上只有原始金额为100时才允许回写,但实际上此时,金额在数据库已经改成28了,这一步的写操作应该不会成功。5.简单的解决方案集合回写的时候,加上初始状态的条件比较。只有保持初始状态不变,才允许成功回写集合。这就是我们常说的“比较并设置”(CAS)。是减少读写锁冲突,保证数据一致性的常用方法。6.业务升级业务线使用CAS解决高并发下数据一致性问题。它只需要在执行设置操作时比较初始值。如果初始值发生变化,则不允许设置成功。对于上面的业务场景,只需要将“UPDAtEt_yueSETmoney=$new_moneyWHEREuid=$uid”升级为“UPDAtEt_yueSETmoney=$new_moneyWHEREuid=$uidANDmoney=$old_money”即可。并发操作时:业务1执行=>UPDAtEt_yueSETmoney=28WHEREuid=$uidANDmoney=100业务2执行=>UPDAtEt_yueSETmoney=38WHEREuid=$uidANDmoney=100【这两个操作当同时运行,只有一个才能执行成功]。7、如何判断set操作哪一次执行成功,哪一次执行失败,其实成功和失败都无所谓,业务可以通过affectrows知道哪些修改不成功:对于业务执行成功,affectrows为1为失败的业务,affectrows为08。总结一下高并发的“查询修改”场景,可以使用CAS(CompareandSet)来解决数据一致性问题。对应业务,就是在设置的时候,加上初始条件的比较。【本文为专栏作者《58神剑》原创稿件,转载请联系原作者】
