《并发扣款,如何保证数据的一致性?》,分享了同一个用户并发扣款时,有一定概率出现数据不一致,可以使用CAS乐观锁的方式,在不降低吞吐量,并且只有少量修改的情况下,保证数据的一致性。
文章发布不到24小时,就有近200的评论。
其中,问的比较多的是ABA问题,这个问题已经在《并发扣款一致性优化,CAS下ABA问题,这个话题还没聊完!!!》中扩展。 其次,问的比较多的是作业题,为什么一定要用 select&set 的方式进行余额写回:UPDATE t_yue SET money=$new_money WHERE uid=$uid AND money=$old_money;
为什么不能采用 直接扣减 的方法:UPDATE t_yue SET money=money-$diff WHERE uid=$uid;
很人说,在并发情况下, 会将money扣成负数 。 为了保证余额不被扣成负数,再加一个where条件:UPDATE t_yue SET money=money-$diff WHERE uid=$uid AND money-$diff>0;
这样是否可行? 画外音:额,撇开业务不谈,这个SQL用列做运算,其实是不好的,建议使用:UPDATE t_yue SET money=money-$diff WHERE uid=$uid AND money>$diff;
画外音:说明绝大部分同学,能够回答正确作业。 聊幂等性之前,先看另一个测试用例的case。 假设有一个服务接口,注册新用户:bool RegisterUser($uid, $name){
//查看uid是否已经存在
select uid from t_user where uid=$uid;
//不是新用户,返回失败
if(rows>0)return false;
else{
//把新用户插入用户表 insert into t_user values($uid, $name); //返回成功 return true; } } 有一个测试工程师,对该接口写了一个测试用例:bool TestCase_RegisterUser(){
//造一些假数据
long uid=123;
String name='shenjian';
//调用被测试的接口
bool result= RegisterUser(uid,name);
//预期注册成功,对结果进行断言判断
Assert(result,true);
//返回测试结果
return result;
}
这是不是一个好的测试用例?这个用例存在什么问题? 你会发现, 相同条件下 ,这个 测试用例执行两次,得到的结果不一样 : (1)第一次执行,第一次造数据,调用接口,注册成功; (2)第二次执行,又造了一次相同的数据,调用接口,注册会失败; 这 不是一个好的测试用例,多次执行结果不同 。 什么是幂等性? 相同条件下,执行同一请求,得到的结果相同,才符合幂等性。 画外音:Google一下,比我解释得更好,但意思应该说清楚了。 如何将上面的测试用例改为符合“幂等性”的测试用例呢? 只需要加一行代码:
bool TestCase_RegisterUser(){
//造一些假数据
long uid=123;
String name=’shenjian’;
//先删除这个伪造的用户
DeleteUser(uid);
//调用被测试的接口
bool result= RegisterUser(uid,name);
//预期注册成功,对结果进行断言判断
Assert(result,true);
//返回测试结果
return result;
}
这样,在相同条件下, 不管这个用例执行多少次,得到的测试结果都是相同的 。 是不是对幂等性有点感觉了。 读请求,一般是幂等的。写请求,视情况而定:
insert x,一般来说不是幂等的,重复插入得到的结果不一定一样
delete x,一般来说是幂等的,删除多次得到的结果仍相同
set a=x是幂等的
set a=a-x不是幂等的
…
result = DoSomething();
if(false==result || TIMEOUT){
//错误,或者超时,重试一次
result= DoSomething();
}
return result;
当然,又会有朋友抬杠了,我从来不重试!!! 画外音:额,这是合格,还是不合格呢? 你可以决定业务代码怎么写,你不能决定底层框架代码怎么写: (1) 站点框架 有没有自动重试? (2) 服务框架 有没有自动重试? (3) 服务连接池 , 数据库连接池 有没有自动重试? 画外音: (1)服务化分层的架构中,建议只入口层重试,服务层不要重试,防止雪崩; (2)dubbo底层,调用超时是默认重试的,这个设计不好; 因此,在有重试的架构体系里,幂等性是需要考虑的一个问题。 现在该懂了,为啥扣款和充值业务,一般使用:select&set,配合CAS方案
set money-=X方案
知其然,知其所以然 ,希望大家有收获。
本文转自“架构师之路”公众号,58沈剑提供。