支付成功后,商家没收到发货通知;用户注册成功,优惠券却没到账——这些异步场景的“数据不一致”,该怎么解决?既不能用TCC/SAGA(同步流程),也不能用2PC(性能差)。今天拆解两种经典异步方案:本地消息表和事务消息。
一、核心场景:异步事务的本质
异步事务的核心是“一个事务成功后,异步触发另一个事务”,且要保证“前者成功,后者必须执行”。比如:
- 场景1:支付服务扣钱成功(本地事务)→ 异步通知订单服务更新状态;
- 场景2:用户注册成功(本地事务)→ 异步通知优惠券服务发放优惠券。
核心需求:即使异步通知失败(比如网络超时、服务宕机),也要保证通知最终能送达,不能丢失。
二、方案1:本地消息表(简单易实现,侵入性强)
本地消息表是“最朴素”的异步事务方案,核心思路是“将异步消息存在本地数据库,和业务事务一起提交,再用定时任务发送消息”。
1. 核心流程(支付成功通知订单)
- 支付服务执行本地事务:扣减用户余额;
- 同时,在支付服务的本地数据库中,插入一条“通知订单”的消息(状态为“待发送”);
- 本地事务提交成功后,启动定时任务(比如每1分钟),扫描“待发送”的消息;
- 定时任务将消息发送到消息队列(如RocketMQ/Kafka);
- 订单服务消费消息,更新订单状态;
- 订单服务消费成功后,回调支付服务,将消息状态改为“已发送”;
- 若消息发送失败(如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

最低0.47元/天 解锁文章
1155

被折叠的 条评论
为什么被折叠?



