分布式事务 - 基于消息传递的分布式事务一致性的实现

场景

以电商场景为例子。
场景1:下单: ①生成订单 -- 本地事务 ②占销售库存 --远程微服务调用
场景2:订单评审通过: ①SSO推结算,②SSO推供应链。
场景3:金融场景。

场景1,2个子事务只要有一个失败,就回滚。
场景2:2个子事务,不管是否失败,都继续补偿重试,如果重试几次还是失败,人工介入。

如果不用成熟的框架(Seata),该如何实现?

本地消息表是一种基于可靠消息最终一致性的分布式事务解决方案,通过将消息持久化与业务操作绑定在同一个本地事务中,确保跨服务操作的最终一致性。以下是其核心实现原理、流程及优化策略:

四、适用场景

  • 最终一致性业务
    ✅ 订单支付后通知发货
    ✅ 用户注册送积分
    ✅ 主库数据变更同步搜索索引。
  • 不适用场景
    ❌ 强一致性要求(如金融转账) ,下单成功占销售库存。
    ❌ 高频事务(消息表写入压力大)。

一、核心原理

  1. 事务原子性保证
    在业务操作(如创建订单)的同一数据库事务中,插入一条消息到本地消息表,利用数据库的ACID特性确保业务数据与消息记录同时成功或回滚

    • 表结构示例
      CREATE TABLE local_message (
        id BIGINT PRIMARY KEY AUTO_INCREMENT,
        biz_id VARCHAR(64) NOT NULL,    -- 业务ID(如订单ID)
        content TEXT NOT NULL,          -- 消息内容(JSON格式)
        status TINYINT NOT NULL DEFAULT 0, -- 状态(0-待发送, 1-已发送, 2-已确认, 3-死亡)
        retry_count INT DEFAULT 0,      -- 重试次数
        created_time DATETIME NOT NULL
      );
      
    • 关键索引:对statuscreated_time建立联合索引,加速扫描效率。
  2. 异步消息投递
    通过定时任务或独立线程扫描状态为待发送(0)的消息,发送至消息队列(如RabbitMQ/Kafka),成功后更新状态为已发送(1);失败则增加重试次数,超阈值后标记为死亡(3)需人工介入。

  3. 消费者幂等性
    消费者需实现幂等逻辑(如唯一业务ID校验),避免消息重复消费导致数据不一致。

    @KafkaListener(topics = "order_created")
    public void consume(Message msg) {
        if (deductLogService.exists(msg.getBizId())) return; // 幂等检查
        inventoryService.deductStock(msg.getContent());      // 业务逻辑
        deductLogService.logSuccess(msg.getBizId());         // 记录消费
    }
    
  4. 补偿机制

    • 发送失败:定时任务重新扫描待发送消息重试投递。
    • 消费失败:MQ重试投递,消费者返回NACK;若持续失败则进入死信队列。
    • 状态不一致:定时对账任务检查已发送但未确认的消息,重新投递或修复状态。

二、工作流程

sequenceDiagram participant A as 事务发起方 participant DB as 业务数据库 participant MQ as 消息队列 participant B as 消费者 A->>DB: 1. 开启事务 A->>DB: 2. 更新业务数据 + 插入消息记录 A->>DB: 3. 提交事务(原子性保证) loop 定时任务 A->>DB: 4. 扫描待发送消息 A->>MQ: 5. 发送消息 MQ-->>A: 6. 发送成功回调 A->>DB: 7. 更新消息状态为“已发送” end MQ->>B: 8. 推送消息 B->>B: 9. 幂等校验 B->>B: 10. 执行业务逻辑 B->>MQ: 11. 返回ACK

三、优缺点分析

优点 缺点 优化策略
1. 实现简单,无需额外中间件(如Seata) 1. 消息延迟(依赖定时扫描,秒~分钟级) 分库分表、批量发送、MQ延迟消息
2. 高可靠性(本地事务保消息必达) 2. 数据库压力大(消息表与业务库耦合) 消息表独立部署、读写分离
3. 业务侵入低(仅需新增消息表) 3. 需消费者实现幂等性 唯一键+状态机、消费日志表

posted @ 2025-03-11 16:25  向着朝阳  阅读(62)  评论(0)    收藏  举报