V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
rihkddd
V2EX  ›  程序员

写 DB 和发送下游消息要放到一个事务里面吗

  •  
  •   rihkddd · 2020-10-19 11:13:35 +08:00 · 3200 次点击
    这是一个创建于 1496 天前的主题,其中的信息可能已经有所发展或是发生改变。

    业务中经常有这种需求,比如新增一个订单,需要发 mq 消息到下游系统。看到把这两个操作放到一个事务的写法,那么这么做能达到这么写的目的吗(写数据 /发消息符合事务的四个特性)?

    个人总觉得这么写是有问题的,但是说不出根本的问题点是什么。大家这种需求一般是怎么实现的?

    19 条回复    2020-10-20 12:35:35 +08:00
    13823133214
        1
    13823133214  
       2020-10-19 11:20:42 +08:00
    确保生产者发送成功
    zczy
        2
    zczy  
       2020-10-19 11:22:35 +08:00
    两套系统单纯加个事务还是有问题的吧,万一 mq 成功了,网络有问题回滚了数据库
    coyove
        3
    coyove  
       2020-10-19 11:26:13 +08:00
    你的担心其实来自于数据库 tx commit 失败的情况,如果不是分布式事务的话这种概率太小了,靠对账和 ack 就可以
    zpfhbyx
        4
    zpfhbyx  
       2020-10-19 11:27:22 +08:00
    mq 不要混到事务里, 生产者失败的话重试或者落 db,事务回滚不依赖 mq...
    hun2008hun
        5
    hun2008hun  
       2020-10-19 11:27:28 +08:00
    发消息是网络请求,结果是(成功、失败、超时),超时的时候本地事务要 commit 还是 rollback 呢?
    zczy
        6
    zczy  
       2020-10-19 11:31:07 +08:00
    幂等 + ack 基本上能保证了吧

    还有什么本地消息表之类的,用数据库事务保证
    rihkddd
        7
    rihkddd  
    OP
       2020-10-19 12:22:42 +08:00
    @zczy
    @zpfhbyx
    @coyove
    @hun2008hun
    感谢各位的回复,从你们的回复看,放到一个事务的做法应该是不对的。
    rihkddd
        8
    rihkddd  
    OP
       2020-10-19 12:23:55 +08:00
    @zczy 是的,去哪儿的 qmq 就是这么个思路实现事务消息特性的。
    GGGG430
        9
    GGGG430  
       2020-10-19 12:48:13 +08:00
    你说的事务是本地事务表吗, 如果是那就是分布式事务的操作, 用本地事务表来保证发送消息成功.
    是不是还有一个定时任务监视本地事务表呢, 正常流程是发送消息成功后删除本地事务表中关联的数据行, 没法送成功则会有后续的定时任务来重发
    jorneyr
        10
    jorneyr  
       2020-10-19 13:24:37 +08:00   ❤️ 1
    1. 落库,标记为待处理
    2. MQ 发送成功
    3. MQ 的回调处理落库的数据,标记为处理完成
    sambawy
        11
    sambawy  
       2020-10-19 13:51:31 +08:00
    不要混在一起,消息发送的成功与否不要影响数据状态的落库,否则会因为一两个消息发送的异常导致一批数据回滚,到时候客户那边就被消息轰炸了
    CoderGeek
        12
    CoderGeek  
       2020-10-19 14:06:32 +08:00   ❤️ 1
    常用手段是本地事务消息, 一般是集中业务库中会有个 localmessage 随着业务比如订单的落库一起保存这 message,然后使用 TransactionSynchronizationAdapter 巴拉巴拉的啥的 保证消息与业务 DB 操作在一个事务,然后应用的线程异步发送 mq 或者半同步这一类的

    问题是性能受影响,下游需要幂等 多余的服务开销 但是做肯定是做的到的
    CoderGeek
        13
    CoderGeek  
       2020-10-19 14:08:56 +08:00
    常用的应该是我说的第一类
    ```java
    begin transaction

    biz_code();

    insertMessage();

    callback();

    end;

    callback 方案可以使用 spring 事务机制进行回调

    function callback(){//异步 or 同步

    var rtn = sendMqMessage();

    If(rtn){

    delMessage();

    }

    }

    对于 callback 发送失败问题,会有定时任务去消息表里面获取未成功发送的消息进行重试,在一定次数还为成功的消息,报警人工干预。
    ```
    nutting
        14
    nutting  
       2020-10-19 15:07:59 +08:00
    发消息可能比较耗时吧,不能放到事务里,我觉得是这个角度考虑?
    js8510
        15
    js8510  
       2020-10-20 09:06:49 +08:00
    同 @jorneyr 的 1,2,3
    zhangdashuan
        16
    zhangdashuan  
       2020-10-20 09:21:53 +08:00
    用事务消息应该可以避过这个问题
    RedBeanIce
        17
    RedBeanIce  
       2020-10-20 09:33:38 +08:00
    看到支付的流程图

    写库,发送消息,调用支付,要放到一个事务里面。

    否则你写了库也是脏数据,没有人回去消费他
    rihkddd
        18
    rihkddd  
    OP
       2020-10-20 12:33:17 +08:00
    @GGGG430 不是的,事务就是 mysql db 的事务,数据是写的业务数据。你说的实现看起来没问题。要讨论的是仅把写数据和发消息两个操作放到一个 db 事务中的做法,能否达到预期效果。
    rihkddd
        19
    rihkddd  
    OP
       2020-10-20 12:35:35 +08:00
    @jorneyr 很标准的流程,就是可能需要在业务表里面维护一下消息的状态~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1593 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 17:03 · PVG 01:03 · LAX 09:03 · JFK 12:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.