您现在的位置是:亿华云 > 人工智能
并发扣款一致性,幂等性问题,这个话题还没聊完!!!
亿华云2025-10-04 00:51:37【人工智能】2人已围观
简介《并发扣款,如何保证数据的一致性?》,分享了同一个用户并发扣款时,有一定概率出现数据不一致,可以使用CAS乐观锁的方式,在不降低吞吐量,并且只有少量修改的情况下,保证数据的一致性。文章发布不到24小时
《并发扣款,扣款如何保证数据的致性一致性?》,分享了同一个用户并发扣款时,性问有一定概率出现数据不一致,话题还没可以使用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 moneymoney=money-$diff WHERE uid=$uid;很人说,在并发情况下,会将money扣成负数。 为了保证余额不被扣成负数,服务器租用再加一个where条件:
UPDATE t_yue SET moneymoney=money-$diff WHERE uid=$uid AND money-$diff>0;这样是否可行?画外音:额,撇开业务不谈,这个SQL用列做运算,其实是不好的,建议使用:
UPDATE t_yue SET moneymoney=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; }这是不是一个好的测试用例?这个用例存在什么问题?
你会发现,相同条件下,这个测试用例执行两次,得到的亿华云计算结果不一样:
第一次执行,第一次造数据,调用接口,注册成功; 第二次执行,又造了一次相同的数据,调用接口,注册会失败;这不是一个好的测试用例,多次执行结果不同。什么是幂等性?
相同条件下,执行同一请求,得到的结果相同,才符合幂等性。
画外音: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不是幂等的 …因此,这么扣减余额:
UPDATE t_yue SET money=$new_money WHERE uid=$uid AND money=$old_money;是幂等操作。
要是这么扣减余额:
UPDATE t_yue SET moneymoney=money-$diff WHERE uid=$uid AND money-$diff>0;不是幂等操作。
聊到这里,或许有朋友要抬杠了,测试用例会重复执行,扣款怎么会重复执行呢?
重试。 重试,是异常处理里很常见的手段。
你在写业务的时候有没有写过这样的代码:
result = DoSomething(); if(false==result || TIMEOUT){ //错误,或者超时,重试一次 result= DoSomething(); } return result;当然,又会有朋友抬杠了,我从来不重试!!!
画外音:额,这是合格,还是不合格呢?
你可以决定业务代码怎么写,你不能决定底层框架代码怎么写:
站点框架有没有自动重试? 服务框架有没有自动重试? 服务连接池,数据库连接池有没有自动重试?画外音:
服务化分层的架构中,建议只入口层重试,服务层不要重试,防止雪崩; dubbo底层,调用超时是默认重试的,这个设计不好;因此,在有重试的架构体系里,幂等性是需要考虑的一个问题。
现在该懂了,为啥扣款和充值业务,一般使用:select&set,配合CAS方案
而不使用:set money-=X方案
画外音:充了100电话费,怎么多了200块?
知其然,知其所以然,希望大家有收获。
【本文为专栏作者“58沈剑”原创稿件,转载请联系原作者】
戳这里,看该作者更多好文
很赞哦!(48827)
相关文章
- 尽量不要在域名中出现特殊字符,这样的域名很容易导致访问者输入错误,同时给人留下不专业的印象,降低网站的可信度,并流失大量潜在客户。
- Python很慢?Python之父一句话亮了
- 算法时间复杂度分析:大O表示法
- StringBuffer和StringBuilder的3个区别
- 打开https://www.aizhan.com/输入自己想要查询的域名然后按回车键,如果做过网站都会有数据显示出来
- 记好这 24 个 ES6 方法,用来解决实际开发的 JS 问题
- 开源平台 GitLab又开始搞事情:大规模封杀开发者账户
- Java 基础 | Object 源码解析
- 注册域名要了解几大点?新手有什么方式注册域名?
- Python五个隐藏的特性,你可能从未听说过