当前位置: 首页 > ds >正文

【Easylive】手动实现分布式事务解决方案流程解析

【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版

分布式事务解决方案深度解析

一、两阶段提交(2PC)

核心流程

  1. 准备阶段

    • 协调者发送prepare请求,参与者执行事务但不提交
    • 参与者锁定资源并记录undo/redo日志
    • 返回Yes/No响应
  2. 提交阶段

    • 全票通过则发送commit命令
    • 任一失败则发送rollback命令

实现方案

-- 事务状态表
CREATE TABLE transaction_log (tx_id VARCHAR(64) PRIMARY KEY,status ENUM('PREPARED', 'COMMITTED', 'ROLLBACKED'),create_time DATETIME
);
// 协调者伪代码
public class Coordinator {public boolean execute(TransactionContext ctx) {// 阶段1:准备for (Participant p : participants) {if (!p.prepare(ctx)) return false;}// 阶段2:提交try {for (Participant p : participants) {p.commit(ctx);}return true;} catch (Exception e) {for (Participant p : participants) {p.rollback(ctx);}return false;}}
}

优缺点

✅ 强一致性保证
❌ 同步阻塞、协调者单点故障
❌ 网络分区可能导致资源锁定


二、补偿事务(TCC)

三阶段操作

阶段操作说明
Try资源预留(如冻结库存)
Confirm确认执行(实际扣减)
Cancel补偿回滚(释放冻结资源)

关键实现

public interface PaymentServiceTCC {@Transactionalboolean tryPayment(Long orderId, BigDecimal amount);@Transactionalboolean confirmPayment(Long orderId);@Transactionalboolean cancelPayment(Long orderId);
}

注意事项

  1. 幂等控制:需处理重复调用
  2. 空补偿:处理未执行Try直接Cancel的情况
  3. 悬挂问题:防止Try超时后Cancel先执行

三、本地消息表

实现架构

[业务事务] → [消息表记录] → [定时任务] → [MQ] → [消费者]

核心代码

CREATE TABLE local_message (id BIGINT AUTO_INCREMENT PRIMARY KEY,biz_id VARCHAR(64) NOT NULL,content TEXT NOT NULL,status TINYINT DEFAULT 0 COMMENT '0-待发送,1-已发送',retry_count INT DEFAULT 0,create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
@Transactional
public void createOrder(Order order) {// 1. 业务操作orderMapper.insert(order);// 2. 记录消息(同事务)LocalMessage msg = new LocalMessage();msg.setBizId(order.getOrderNo());msg.setContent(JSON.toJSONString(order));messageMapper.insert(msg);
}

优势

  • 实现简单,与业务解耦
  • 天然支持重试机制

四、Saga模式

执行模式对比

类型特点
协同式通过事件驱动协调
编排式中央协调器控制流程

状态机示例

public void placeOrder() {try {// 正向操作inventoryService.reserveStock();paymentService.charge();shippingService.createShipment();} catch (Exception e) {// 逆向补偿shippingService.cancelShipment();paymentService.refund();inventoryService.releaseStock();}
}

日志追踪设计

CREATE TABLE saga_log (saga_id VARCHAR(64) NOT NULL,step_name VARCHAR(32) NOT NULL,status VARCHAR(16) NOT NULL,params TEXT,create_time TIMESTAMP,PRIMARY KEY (saga_id, step_name)
);

方案选型指南

对比矩阵

方案一致性复杂度性能适用场景
2PC强一致银行转账等强一致场景
TCC最终一致中高电商交易、支付系统
本地消息表最终一致物流通知、积分系统
Saga最终一致中高长流程业务(保险理赔)

组合方案推荐

  1. 支付+库存:TCC + 本地消息表
  2. 订单履约:Saga + 异步补偿
  3. 数据一致性:2PC + 超时补偿机制

最佳实践原则

  1. 业务分析:根据CAP理论权衡一致性需求
  2. 降级方案:设计合理的补偿机制
  3. 监控体系:建立事务状态追踪看板
  4. 压力测试:验证方案在高并发下的表现

注:所有方案都需要配合幂等控制、重试机制和日志追踪才能保证可靠性

本地消息表(Local Message Table)方案专业解析

1. 核心设计思想

本地消息表是一种基于最终一致性的分布式事务解决方案,通过异步消息传递+事务日志实现跨服务数据同步:

  • 事务拆分:将分布式事务拆分为多个本地事务
  • 消息驱动:通过本地消息表记录待处理操作,异步触发下游服务
  • 重试补偿:定时任务保证消息可靠投递,失败时自动重试

2. 技术实现组件

组件作用技术实现示例
业务事务表记录主业务数据(如订单表)MySQL order
本地消息表存储待分发的消息(状态机模式)MySQL local_message
定时任务调度器扫描未处理消息,触发下游服务调用Spring @Scheduled + 线程池
幂等处理器防止下游服务重复消费Redis唯一ID/数据库唯一约束
监控告警模块捕获长期失败消息,触发人工干预Prometheus + Grafana + 企业微信报警

3. 关键流程(ACID特性保障)

Client ServiceA LocalDB Scheduler ServiceB 创建订单 开启事务 INSERT INTO order (...) VALUES (...) INSERT INTO local_message (...) VALUES (...) 提交事务 返回成功 SELECT * FROM local_message WHERE status=0 返回待处理消息 HTTP POST /api/process 200 OK UPDATE local_message SET status=1 UPDATE retry_count=retry_count+1 alt [调用成功] [调用失败] loop [定时任务] Client ServiceA LocalDB Scheduler ServiceB

4. 技术关键点

原子性(Atomicity)保障

@Transactional
public void createOrder(Order order) {orderDao.insert(order);          // 业务数据messageDao.insert(toMessage(order)); // 事务消息
}

可靠性(Durability)设计

  • 消息表与业务表同库同实例,利用RDBMS的WAL日志保证持久化
  • 定时任务采用至少一次(at-least-once)投递语义

幂等性(Idempotency)控制

@PostMapping("/api/process")
public Response process(@RequestBody Message msg) {if (redis.setnx(msg.getId(), "1", 24h)) { // 分布式锁realProcess(msg); // 真实业务逻辑}return Response.success();
}

一致性(Consistency)恢复

long delay = Math.min(1000 * Math.pow(2, retryCount), 3600000);

5. 生产级优化建议

消息表分库分表

CREATE TABLE local_message_${hash(id)%16} (...);

批量消息处理

@Scheduled(fixedDelay = 5000)
public void batchProcess() {List<Message> batch = messageDao.scan(100);CompletableFuture[] futures = batch.stream().map(msg -> asyncProcess(msg)).toArray(CompletableFuture[]::new);CompletableFuture.allOf(futures).join();
}

死信队列处理

if (msg.getRetryCount() > MAX_RETRY) {deadLetterQueue.add(msg); // 转入死信队列alarmService.notifyAdmin(msg);
}

6. 方案局限性

  1. 时效性缺陷
    依赖定时任务扫描,消息处理延迟通常在秒级

  2. 架构约束
    要求业务消息必须可序列化存储

  3. 维护成本
    需额外维护消息表、定时任务等组件

7. 适用场景评估

场景适用性理由
订单创建→库存扣减★★★★★允许短暂延迟,业务容忍最终一致
支付成功→短信通知★★★★☆通知类操作对实时性要求较低
金融账户转账★★☆☆☆需要强一致性,建议使用TCC或Saga
日志数据同步★★★★★天然适合异步处理

该方案在电商、物流等互联网业务中广泛应用,是平衡实现复杂度与可靠性的典型折中方案

🍱 用"外卖订餐"理解本地消息表

现实场景 vs 技术实现

餐馆运营问题分布式系统问题解决方案
前台接单记录业务数据存储MySQL订单表
厨房小票事务消息local_message表
服务员送小票消息投递定时任务扫描
厨房小黑板幂等控制Redis唯一标识
店长监督监控告警Prometheus+钉钉

核心四步流程

1️⃣ 接单存双录(事务原子性)

@Transactional // 原子操作保证
public void 接单(订单 order) {// 记录主订单(账本)订单库.save(order); // 生成厨房小票(消息)小票机.save(new 小票(order.id, "新订单", LocalDateTime.now()));
}

💡 就像收银机:按一次按钮同时打印顾客账单和厨房小票

2️⃣ 异步送小票(最终一致性)

每5分钟
成功
失败
定时任务
扫描未送小票
是否超过3次?
尝试送厨房
放入死信队列
标记已送达
增加重试次数

3️⃣ 厨房防重做(幂等性)

def 做菜(订单号):if redis.get(订单号) == "处理中":return "已在制作"redis.set(订单号, "处理中", ex=3600)实际做菜操作()return "开始制作"

👨‍🍳 相当于厨师长看到相同订单号会说:“这份已经在炒了!”

4️⃣ 异常处理三板斧

// 1. 指数退避重试
Thread.sleep(1000 * Math.pow(2, 重试次数));// 2. 死信队列监控
if(小票.重试次数 > 3){钉钉报警("请店长处理订单:"+小票.订单号);
}// 3. 人工补偿入口
@PostMapping("/手动重试")
public String 人工重试(String 订单号){消息 msg = 小票机.find(订单号);厨房服务.做菜(msg.getContent());
}

🌟 方案优势

  1. 高可靠:小票机相当于WAL日志,断电也不丢单
  2. 可扩展:多个服务员(消费者)并行处理小票
  3. 解耦合:厨房装修(服务升级)不影响前台接单
  4. 可追溯:所有小票永久存档,随时审计

🚨 注意事项

  1. 小票内容要包含全部必要信息(如顾客忌口)
  2. 厨房处理能力要匹配送单频率(背压问题)
  3. 定期归档历史小票(消息表分库策略)

🍜 就像优秀的外卖系统:订单可能稍有延迟,但绝不会丢失或重复!
用这种模式可以处理:订单→库存、支付→通知等大多数最终一致性场景。

http://www.xdnf.cn/news/1040.html

相关文章:

  • Java转Go日记(四):socket编程
  • STM32之DHT11温湿度传感器---附代码
  • vue3性能优化
  • 【数据结构_12】优先级队列
  • 深度学习3.5 图像分类数据集
  • YOLO11改进 | 特征融合Neck篇之Lowlevel Feature Alignment机制:多尺度检测的革新性突破
  • C 语言开发问题:使用 <assert.h> 时,定义的 #define NDEBUG 不生效
  • vin码识别技术-车辆vin识别代码-Java接口集成
  • 《理解C++宏:从#define到条件编译》
  • 【工具】VS Code/Cursor 编辑器状态栏颜色自定义指南
  • 装饰模式:动态扩展对象功能的优雅设计
  • AI Agent开发第35课-揭秘RAG系统的致命漏洞与防御策略
  • 极刻AI搜v1.0 问一次问题 AI工具一起答
  • 城市客运安全员证适用岗位及要求
  • QtCreator的设计器、预览功能能看到程序图标,编译运行后图标消失
  • 关于金碟云星空批号问题
  • 自动化测试
  • Psychology 101 期末测验(附答案)
  • Ubuntu 系统下安装和使用性能分析工具 perf
  • HarmonyOS-ArkUI: animateTo 显式动画
  • Git SSH 密钥多个 Git 来源
  • 承兑汇票文字录入解决方案-承兑汇票识别接口-C++集成方式
  • SQL优化
  • 安卓逆向工程:从APK到内核的层级技术解析
  • 聚客AI万字解密AI-Agent大模型智能体:从架构设计到工业落地的全栈指南
  • 算法题(130):激光炸弹
  • 力扣刷题Day 23:最长连续序列(128)
  • Azkaban集群搭建
  • 基于Python的图片/签名转CAD小工具开发方案
  • 13.电阻在EMC设计中的妙用