V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
HonoSV
V2EX  ›  程序员

关于后端接口(幂等性?防并发?)的问题

  •  
  •   HonoSV · 2019-10-28 14:51:59 +08:00 · 3035 次点击
    这是一个创建于 1888 天前的主题,其中的信息可能已经有所发展或是发生改变。

    菜鸟最近遇到一种场景,有点想不明白如何处理:

    设计一个接口从账户 A 转账到账户 B,一次只能有一笔没走完的流水(中间流程很长)。

    如何防止同一个账户( A )在同一时间重复提交多笔流水呢?

    目前我能想到的解决方法就是加锁,但是效率低下。

    求问一下 V 站大佬,这种场景一般如何解决呢?

    13 条回复    2019-10-29 11:02:03 +08:00
    lihongjie0209
        1
    lihongjie0209  
       2019-10-28 14:56:24 +08:00
    需求就是一个账户只能有一笔流水, 为什么会效率低呢? 你难道用的是全局锁?
    HonoSV
        2
    HonoSV  
    OP
       2019-10-28 14:59:51 +08:00
    @lihongjie0209 哈哈,是全局锁
    opengps
        3
    opengps  
       2019-10-28 14:59:54 +08:00
    冒充一下大牛回答下:提交方做提交标识,后端用标识做幂等性校验。( https://www.opengps.cn/Blog/View.aspx?id=426 ,欢迎点评 )
    要求标识需要:
    前端处理到位:一个页面,不返回成功怎么提交都是同一个标识。处理成功则刷新标识
    后端处理到位:对处理的标识,加锁防止并发争用,同一个标识只执行一次,多次提交返回相同结果
    evoluc
        4
    evoluc  
       2019-10-28 15:05:54 +08:00
    这个用个乐观锁?
    baiyi
        5
    baiyi  
       2019-10-28 15:10:48 +08:00
    转账这个操作在接口设计上肯定不是幂等的,可以用 3 楼说的,client_token 做校验
    wangyzj
        6
    wangyzj  
       2019-10-28 15:11:11 +08:00
    tcc
    lihongjie0209
        7
    lihongjie0209  
       2019-10-28 15:18:21 +08:00
    @HonoSV #2 那你把锁缩小到账户就好了, 每个账户持有一把锁
    luozic
        8
    luozic  
       2019-10-28 15:23:29 +08:00
    这个你可以看系统线程调度同步的那个思想,信号量和互斥锁,至于这个具体实现可以前后端也可以纯后端。
    polythene
        9
    polythene  
       2019-10-28 15:27:18 +08:00
    幂等的最简单的一个方法就是为每个请求 /交易创建一个 ID,在真正执行前检查一下
    laminux29
        10
    laminux29  
       2019-10-28 16:05:29 +08:00
    你的这个需求,可以通过分发唯一 Tick ( ID,或 uint64 )来解决,也就是每一笔账单、每一个处理环节,都会有一个唯一的 Tick,这样账单与处理就不会出错了。

    接着,性能压力,也转移到 Tick 组件。

    最后,Tick 组件这一块又有非常成熟的方法来处理性能问题。比如提前生成、批量分发等等。
    crclz
        11
    crclz  
       2019-10-28 22:08:37 +08:00
    这跟幂等没啥关系。
    加应用锁(有业务意义的锁),具体实现可以是分布式锁,用来锁住某个人的某个业务。例如 key="user:66 category=transfer-money"。也可以是对关系型数据库里面的某条记录加 X 锁(独占锁),例如新建一个专门加锁的表。

    如果这个转账是多阶段的、多个事务的,那么就应该将数据库里面某个字段作为标志位,在第一个事务里面将标志位设为 1,在最后一个事务结束后设置为 0。其他笔转账想要开启第一个事务的话,首先检测标志位是否为 0,如果不为 0,则表明有一笔处理中的转账。
    加锁效率不低。因为没有锁住热点数据,没有争用。
    OldCarMan
        12
    OldCarMan  
       2019-10-29 02:43:58 +08:00
    11 楼正解。
    dany813
        13
    dany813  
       2019-10-29 11:02:03 +08:00
    每个请求带个 request_id ?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   975 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 23:11 · PVG 07:11 · LAX 15:11 · JFK 18:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.