CAS解决数据一致性问题

什么是CAS?“Compare And Set”(CAS),是一种常见的降低读写锁冲突,保证数据一致性的方法。
数据一致性是我们开发时必须注意的问题,特别涉及到钱这块。
这里举一个用户购物的例子:
现有一个用户信息表user_account,表中字段为id,uid,amount。
表中现有用户甲,余额100元。
现在甲想购买一个价值10元(并打9折)的商品,那么流程如下:
1.select amount from user_account where uid = $uid;
查询出甲的余额: $old_amount为100元
2.业务逻辑计算

if ($old_amount >= 10 * 0.9 ){
     $new_amount = $old_amount - 10 * 0.9;
     //进行下一步
} else {
     return false;
}

3.update user_account set amount = $new_amount where uid = $uid

更新数据用户余额
以上这几步在单机或者并发量低的情况下是没有问题的,但是随着系统流量数据变大,在高并发的分布式环境下,这种“查询+修改”的业务就容易引发一致性问题。
比如这种情况:
有两个业务,一个业务A想购买一样物品(20元打8折),一个业务B也想购买一样物品(30元,打7折)
流程如下:
1.业务A,业务B同时去查数据库,此时$old_amount都为100元
2.业务A算出自己付款后余额100-20 * 0.8=84,业务B算出自己付款后余额100-30 * 0.7=79元
3.业务A更新数据库amount=84,业务B更新数据库amount=79
最终用户数据库余额amount为79,这肯定是不对的。
如何解决这种问题?
就要用到CAS了,我们在set写入数据的时候,可以加上amount初始状态的条件compare,只有amount不变时,才允许set写入成功。
这里我们将update user_account set amount = $new_amount where uid = $uid改为update user_account set amount = $new_amount where uid = $uid and amount = $old_amount即可

在并发情况下
业务A执行 update user_account set amount = 84 where uid = $uid and amount=100
业务B执行 update user_account set amount = 79 where uid = $uid and amount=100
上述两条sql是互斥的,只可能有一条执行成功
这里引申出一个问题,系统如何知道哪个成功哪个失败?
数据库数据更新后,我们可以从数据库那里获取到受影响的行数即affect rows,那么affect rows为1业务执行成功,affect rows为0则执行失败

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章