异步事务方案:本地消息表vs事务消息

支付成功后,商家没收到发货通知;用户注册成功,优惠券却没到账——这些异步场景的“数据不一致”,该怎么解决?既不能用TCC/SAGA(同步流程),也不能用2PC(性能差)。今天拆解两种经典异步方案:本地消息表和事务消息。

一、核心场景:异步事务的本质

异步事务的核心是“一个事务成功后,异步触发另一个事务”,且要保证“前者成功,后者必须执行”。比如:

  • 场景1:支付服务扣钱成功(本地事务)→ 异步通知订单服务更新状态;
  • 场景2:用户注册成功(本地事务)→ 异步通知优惠券服务发放优惠券。

核心需求:即使异步通知失败(比如网络超时、服务宕机),也要保证通知最终能送达,不能丢失。

二、方案1:本地消息表(简单易实现,侵入性强)

本地消息表是“最朴素”的异步事务方案,核心思路是“将异步消息存在本地数据库,和业务事务一起提交,再用定时任务发送消息”。

1. 核心流程(支付成功通知订单)

  1. 支付服务执行本地事务:扣减用户余额;
  2. 同时,在支付服务的本地数据库中,插入一条“通知订单”的消息(状态为“待发送”);
  3. 本地事务提交成功后,启动定时任务(比如每1分钟),扫描“待发送”的消息;
  4. 定时任务将消息发送到消息队列(如RocketMQ/Kafka);
  5. 订单服务消费消息,更新订单状态;
  6. 订单服务消费成功后,回调支付服务,将消息状态改为“已发送”;
  7. 若消息发送失败(如MQ宕机),定时任务会重试(最多3次),超过次数则人工介入。

2. 实战:本地消息表实现(Spring Boot+MySQL+RocketMQ)

2.1 数据库表设计(支付服务本地表)

-- 本地消息表
CREATE TABLE `local_message` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  `message_id` varchar(64) NOT NULL COMMENT '消息唯一ID',
  `business_type` varchar(32) NOT NULL COMMENT '业务类型(如PAY_NOTIFY_ORDER)',
  `business_id` varchar(64) NOT NULL COMMENT '业务ID(如订单号)',
  `message_content` text NOT NULL COMMENT '消息内容(JSON格式)',
  `status` tinyint NOT NULL COMMENT '状态:0-待发送,1-已发送,2-发送失败',
  `retry_count` int NOT NULL DEFAULT '0' COMMENT '重试次数',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_message_id` (`message_id`),
  KEY `idx_status_create_time` (`status`,`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='本地消息表';

2.2 核心代码(支付服务)

@Service
public class PayService {
    @A
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李景琰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值