zoukankan      html  css  js  c++  java
  • 高性能余额变更方案演化

    背景:
    交易系统账户服务,存在着A与B之间互相转账的需求,如一笔转账交易
    (A+100元,B-100元),A和B其中一方可以为普通用户,也可以为商户,两种角色都可以加款,可以扣款,余额不允许扣成负数。

    方案1:
    每次按账户ID更新余额表。
    为了防止对某账户余额的并发更新,可以采用悲观锁机制,如:

    • 锁定行记录 for update
    • 使用账户Redis分布式锁
    • 使用账户Zookeeper分布式锁
      也可以改进为采用乐观锁机制,如对余额表增加version字段,每次更新时version+1,比如当前版号为5,则sql为:
           update 余额表 set amount = amount + 100 where account_id = xxx and version = 5;
    

    缺点:

    • update并发不高
    • 涉及到A、B转账操作时,假如A是热点账户(商户),则并发冲突急剧增加
      方案2:
      开发童鞋参考了某大型电商库存方案并多次讨论后得出以下架构

    缺点:

    • 热点账户余额查询业务上需忍受非实时
    • 热点账户可能被扣成负数(超扣),也可能会扣不完(少扣)
    • 复杂性提升相当高,开发、运维成本陡增

    方案3:

    查询余额实现:

      select amount from 余额表 where account_id = xxx;
      +
      select sum(amount) from 在途余额表  where is_handle = 0 and account_id = xxx;
    

    可能存在的问题(假设存在以下时序场景):

      select amount from 余额表 where account_id = xxx;
      update 余额表 set amount = amount + 1 where account_id = xxx;
      select sum(amount) from 在途余额表  where is_handle = 0 and account_id = xxx;
    

    即在查询完余额表,在未查询在途额余额表前那一刻,刚好有一次update操作,即数据版本发生了变更,会导致查询出来的余额不准。
    待优化方案:增加版本号,保证余额表和在途余额表是同一个版本

      select amount, version from 余额表 where account_id = xxx;
      +
      select sum(amount) from 在途额余额表 where version >= $version and account_id = xxx;
    

    注:
    1、在途余额表在insert时使用Long.MAX_VALUE
    2、后台定时任务在处理在途额余额表时将当前余额表的version更新到在途余额表的version字段,同时将余额表version+1

    总结: 架构设计不能脱离具体的业务场景,技术架构服务于具体业务。另外即使网上找到的适合大厂的方案,也要根据公司现有开发人力、业务量、运维能力等进行综合考量。

    遗留问题思考: 假如单表insert达到瓶颈,如何伸缩?

    欢迎转载,转载请务必注明出处
  • 相关阅读:
    轮播图
    MySQL初认识
    css的动画
    jQuery 滚动监听总结
    DOM操作 练习
    Ajax总结
    日历 练习
    Jquery基础
    2018-07-14Java基础+基本数据类型+自动/强制数据类型转换+定义变量+运算符
    2018-07-13E-R图设计数据库+三大范式+修改用户密码+分配用户权限
  • 原文地址:https://www.cnblogs.com/mzsg/p/11977633.html
Copyright © 2011-2022 走看看