先查询后修改并发的时候sql_如何解决并发场景下扣款的数据一致性问题?

 2023-09-09 阅读 19 评论 0

摘要:1、场景介绍场景1:扣费,企业账户送流量或者红包,用户签到领取。此场景下就是多用户对某一个账号的并发扣款;场景2:充值,打赏给主播,这种场景是多用户对同一个账号进行打款,但是方案和问题和场景1是一致的。2、场景举

e2f0c656832f4a11d0bcd03bac96ad1d.png

1、场景介绍

场景1:扣费,企业账户送流量或者红包,用户签到领取。此场景下就是多用户对某一个账号的并发扣款;

场景2:充值,打赏给主播,这种场景是多用户对同一个账号进行打款,但是方案和问题和场景1是一致的。

2、场景举例

sql一对多查询需要返回一和多、假设有两个业务操作同一个账号,账号余额为100,业务1扣除50,业务2扣除40,如果顺利应该是剩余100-50-40=10,那么我们看如下并发操作的场景:

adb2e423a139b8b3545d7f70c10a40f7.png

通过两个业务的并发操作,最后账户余额为60(是业务2最后修改后的余额值)。

3、解决方案

3.1 分布式锁

多表联合查询sql优化?由于是分布式环境,采用分布式锁可以保证数据一致性,但是这是小概率事件,并且引入新组件(redis/zk),还会降低吞吐量。

分布式锁参考:https://blog.csdn.net/unclecoco/article/details/99442998

3.2 悲观锁

在查询语句加 for update,行记录加上排它锁,这样后来的事务会阻塞查询,这样就避免了数据不一致。

//开始事务

sql数值处理函数。3.3 乐观锁

CAS(Compare And Swap):内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

通过CAS操作,即旧值和预期值相同时执行修改,例如:

update t_account set money=#{new_money} where id=#{id};

sql server修改表数据。修改为:

Update t_account set money=#{new_money} where id=#{id} and money=#{old_money};

这样在并发情况下,只能有一个修改成功,affect row为1;其他事务由于money不等于旧值,修改失败,affect row为0。

3.4 为什么不使用减等于的sql操作

oracle并发、例如 :

update t_account set money= money - $spend where id = #{id} ;

这里要再加上余额的判断避免出现负数金额:

Update t_account set money= money- $spend where id = #{id}} and money>= $spend ;

sql子查询嵌套优化?稍微改一下这里的更新语句也能完成正确的更新就算是并发也都将正常。

但是这样做将产生一个问题不幂等。

什么是不幂等 ?

在相同的条件下,执行同一请求,得到的结果相同才符合幂等性。

sql数据库备份,如果是money= money- $spend的操作在多次操作场景下就会产生重复扣款。

3.5 ABA问题

ABA问题是由CAS衍生来的,在并发极端情况下会产生,那么什么是ABA问题?

标准的描述如下:

sqlserver最大并发数,并发1(上):获取出数据的初始值是A,后续计划实施CAS乐观锁,期望数据仍是A的时候,修改才能成功;

并发2:将数据修改成B;

并发3:将数据修改回A。

并发1(下):CAS乐观锁,检测发现初始值还是A,进行数据修改。

sql并发控制。上述并发环境下,并发1在修改数据时,虽然还是A,但已经不是初始条件的A了,中间发生了A变B,B又变A的变化,此A已经非彼A,数据却成功修改,可能导致错误,这就是CAS引发的所谓的ABA问题。

举一个游戏充值例子,如下:

261d5e7b6823e081120864f69ec8568e.png

按照业务的诉求,事务3是最终将数据库结果值恢复为100,但是通过cas比较更新最终结果出现了40,这个就是ABA导致的问题。

update子查询返回的值不止一个,我们还是以前面的sql作为例子讲解ABA问题如何解决?

1、数据库表结构由(id、money)修改为(id、money、version)

2、数据库查询由之前的:

select money from t_account where id=#{id}

修改为:

select money,version from t_account where id=${id}

3、数据库修改除了cas比较外,还需要版本相同,并且进行版本修改:

update t_account set money=#{new_money} where id=#{id} and money=#{old_money};

修改为:

update t_account set money=#{new_money} where id=#{id} and money=#{old_money} and version=#{version_old}

4、总结

select&set业务场景,在并发时会出现一致性问题;

基于“值”的CAS乐观锁,可能导致ABA问题;

不能采用减等于的sql操作,由于此操作不幂等;

CAS乐观锁,必须保证修改时的“此数据”就是“彼数据”,应该由“值”比对,优化为“版本号”比对。

4d43ef290cbcf753776cb1226a8a264d.png

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/29135.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息