0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

并发场景下如何保证数据操作的准确性?

jf_ro2CN3Fa 来源:juejin.cn 2023-07-09 16:02 次阅读

  • Part One
  • Part Two


Part One

我们假想一个场景,在并发场景下,假如要对账户余额(tb_balance.balance)进行增加和减少余额,要怎么设计才能保证数据不出错呢?

下面是我的一些设想(假设余额更新20或减少20):

直接sql更新

直接balance自增20,怎么并发都余额都不会错了是不是~,但这里会有问题,1.假如是减余额的话,要注意余额不能小于0 2.假如后续需要用这个余额结果再进行一些业务操作的话,是取不到这个余额的sql

updatetb_balancesetbalance=balance+20wherebalance=#{old_bal}anduser_id=#{user_id}

CAS

经典的compare and swap,就是入库时去对比旧值是否与现数据库中的值相等,若相等则更新否则不更新,但是会有风险,就是经典的aba问题。假如更新返回的影响数为0的话,说明余额已经发生了变化,所以需要抛出异常并进行重试。(这里需要写一些补偿的业务逻辑去处理余额更新失败的问题)

/**
*用户余额增加20
*newBal为新值
*oldBal为库中查出的值
**/
oldBal=balService.getBal(userId);
newBal=oldBal+20;

intcount=balService.updateBal(newBal,oldBal,userId);
Assert.businessInvalid(count==0,"updateerror");

//todosomebuisness
updatetb_balancesetbalance=#{new_bal}wherebalance=#{old_bal}anduser_id=#{user_id}

乐观锁

乐观锁其实就是使用version去进行版本控制,在更新时判断是否更新数据的版本与现库内版本是一致的,若一致则更改并上升版本号,否则认为在此期间有其它线程进行数据更新。假如更新失败的话,同样进行重试处理。这样的好处就是可以避免aba问题,同时也可以使用增加后的余额进行后续的操作。目前已知有些公司就是这么操作的

try{
//dosomebuisness
intcount=balService.updateBal(newBal,old_version,new_version,userId);
Assert.businessInvalid(count==0,"updateerror");
//dosomebuisness
}catch(){
//iffailtodosomethingtryagain
}
updatetb_balancesetbalance=#{balance}andbalance=#{new_version}whereversion=#{old_version}anduser_id=#{user_id}

--ifaffectcount>1success
--ifaffectcount=0retry

redission分布式锁

像集群的项目,我们经常会使用分布式锁去保证幂等性,如果只有单一接口会去操作账户余额那使用分布式锁没有问题,只要在该接口加上锁,保证同一时间只有单一线程进行该业务操作即可;但往往实际业务场景并不会那么简单,比如一个商城,可能会有几十上百个入口可以对余额进行变更,比如定时扣费、订单支付、替他人代付等等;那其实可以通过面向对象的思想,将余额的操作进行抽象,抽象出一个余额类,将该类的方法加上锁,然后所有的业务都强制通过该类进行余额变更的操作,即可保证操作的可靠性。

所以也就是说,订单系统、服务管理中心等等服务都不应该能直接操作到余额数据,还是得抽出一个类型财务中台的东东去统一处理余额,然后财务中台中又维护这么一个余额类去保证更新的可靠性? 这其实也就是软件工程中的单一入口 思想吧

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

Part Two

业务背景:现在实际开发过程中会有这样一种场景,订单表a作为订单业务核心表,而会有a1,a2,a3...an个业务会去更新订单表a的状态,而同时a.status(订单状态)也是作为b,c,d...业务操作的核心判断依据(也就是在不同的订单状态下可能做的操作是截然相反的)

在代码层面上,我们一般的写法是直接用注解开个事务,以保证操作的原子性。这种写法目前让我们的业务还是平稳运行的~

//@apiLock是redis锁
@ApiLock
@Transactional
publicvoiddosomething(){

}

那么假设用户量激增,并发量暴涨,那么会出现什么情况呢?

搞事情的时间来了,事务只能保证单个操作的事务性,假如并发量高的时候,可能就会出现(假设b业务的前提是a订单再待接单[wait_acceipt]的状态):

b业务查询时,a.status=wait_acceipt,然后b业务开始处理;而此时,a1业务对a订单进行操作,并且先于b业务处理完,这时a订单走到了待服务(wait_service),而b还在进行业务处理;然后b业务处理完成,并进行提交。

那么就会产生很多异常的数据了,而且影响范围会很大

解决方案&一些思考

我们可以关注到,这个场景的问题点其实并不在数据的插入与更新,而是在读取,在业务处理过程怎么在确定订单状态没有发生改变

使用redis锁,在读取订单时候对订单进行上锁,业务结束之后再释放

单一入口 :将订单类的操作抽象成一个order类,然后在这个order类中去进行查询 操作,或者在不同服务中用一个redis-key也是OK的

publicclassOrderService{

lock()

select()

unlock()

}
publicvoiddosomeB(){
try{
orderService.lock(serviceOrderId);
//todo
}finally(){
orderService.unlock(serviceOrderId);
}
}

使用mysql共享锁(读锁),在数据库层面进行阻塞,更加精准,并且不使用单一入口也是可以的,但是可能存在索引失效锁表的风险(核心表被锁就炸了)

publicclassOrderService{
/**
*读取并且上锁
*/
selectAndLock()

/**
*读取
*/
select()
}
--读锁会阻塞写(X),但是不会堵塞读(S),在事务提交后,读锁会自动释放
selectxxxfromorderwhereservice_order_id=xxxlockinsharemode;

总体来说 ,思想就是对正在操作的数据进行加读锁,阻塞其它的线程。当然这种方案也是双刃剑,毕竟会减少吞吐量,还是应该进行业务梳理,确定加锁的必要性,避免过分设计~像现在的系统,我就没去动它,hh~


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据库
    +关注

    关注

    7

    文章

    3643

    浏览量

    63748
  • Version
    +关注

    关注

    0

    文章

    32

    浏览量

    7521

原文标题:并发场景下如何保证数据操作的准确性?

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何保证填报数据准确性?#数据填报 #光点科技

    数据
    光点科技
    发布于 :2023年10月25日 17:12:19

    如何知道ds18b20测温结果的准确性

    如何知道ds18b20测温结果的准确性,除了用冰水混合物检测
    发表于 12-02 16:51

    系统快速、稳定性和准确性之间的权衡

    衡量一个测控系统的好坏的标准主要在于该系统的快速、稳定性和准确性,但一般常常会得此失彼,比如我要满足系统准确性,可对一段时间采集的数据进行处理与分析,去除干扰信号,再得到一个
    发表于 07-28 11:42

    如何提高工程预算的准确性

    工程量。工程量的计算是编好施工图预算的主要环节,是整个预算编制过程中最繁杂的一个工序,用工时最多,出错可能也最多,而工程量又是整个预算的主要数据,是计算的基础,因此,一定要抓好工程量的准确性
    发表于 07-25 17:35

    合同智能审核软件-提高审查效率和准确性

    审查的准确性。使用软件能够提高审查速度在提高合同审查的速度方面,灵玖合同智能审核软件设定了丰富的合同审核范围:1.审查合同格式是否规范:检查内容包括字体字号行距,章节表格序号,上下标和大小写
    发表于 09-05 17:22

    NI公司u***-4065的准确性和速度问题

    我使用labview2017编程和u***-4065来测量通过继电器切换的多条线的电阻。现在有两个问题,一个是准确性,另一个是速度。对于准确性。我测量的线路电阻具有参考值。我经常遇到绝缘电阻,它
    发表于 11-24 13:24

    如何测试运算放大器噪声模型的准确性

    如何测试运算放大器噪声模型的准确性?如何建立自己的噪声模型?
    发表于 04-12 06:08

    如何提高交流脉冲对HPMMM充磁和去磁时磁场定向的准确性

    如何提高交流脉冲对HPMMM充磁和去磁时磁场定向的准确性
    发表于 11-19 06:30

    如何确认延时函数时间的准确性

    如何确认延时函数时间的准确性
    发表于 01-27 07:35

    如何校准STM32F407VG RTC精度以获得更高的准确性呢?

    我们是否能够校准 RTC 以获得更高的准确性?如何解决STM32F407VG RTC精度问题?
    发表于 12-28 11:13

    越南VNTA部署基于AI的射频测试系统,保证测试的准确性和一致性

    由R&S BTC和RFSpark组成的自动化射频测试系统可优化人力资本投资,并保证测试的准确性和一致性。
    的头像 发表于 05-15 16:17 3429次阅读

    保证双法兰液位计测量的准确性的方法

    为了保证双法兰液位计液位测量的准确性,必须首先保证容器内气相、液相介质对差压变送器高、低压侧作用压力传递的准确性,从压力作用的起点出发,根据双法兰液位计的组成部分和工作原理来分析应用中
    发表于 07-19 11:59 2014次阅读

    土壤紧实度仪是什么,它的准确性如何来保证

    随着科学技术的不断发展,仪器以及检测技术已经成为当代的主流。土壤紧实度仪作为一款土壤检测仪器,其检测的准确性对于农业生产以及实验研究来说具有重要的意义。那么,对于土壤紧实度的准确性该如何保证呢?下面
    的头像 发表于 01-06 11:22 2329次阅读

    制造行业BOM如何保证准确性

    制造行业BOM如何保证准确性:使用专业的BOM软件可以帮助制造商创建准确的BOM。这种软件不仅可以自动检查错误,还可以轻松地更新和修改BOM。比如,彩虹企业图纸管理软件​,系统中...
    的头像 发表于 03-27 11:02 713次阅读
    制造行业BOM如何<b class='flag-5'>保证</b><b class='flag-5'>准确性</b>?

    怎样测试电流探头的准确性以及保证其精准性

    随着科技的不断发展,电流探头的应用范围越来越广泛。在各种电子产品的设计和生产过程中,电流探头的准确性对于检测和测量电流显得尤为重要。那么,怎样测试电流探头的准确性以及保证其精准性呢? 首先,我们需要
    的头像 发表于 12-14 10:49 345次阅读
    怎样测试电流探头的<b class='flag-5'>准确性</b>以及<b class='flag-5'>保证</b>其精准性