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

发布时间:2026/8/1 20:58:14
【Easylive】手动实现分布式事务解决方案流程解析 【Easylive】项目常见问题解答自用持续更新中… 汇总版分布式事务解决方案深度解析一、两阶段提交2PC核心流程准备阶段协调者发送prepare请求参与者执行事务但不提交参与者锁定资源并记录undo/redo日志返回Yes/No响应提交阶段全票通过则发送commit命令任一失败则发送rollback命令实现方案-- 事务状态表CREATETABLEtransaction_log(tx_idVARCHAR(64)PRIMARYKEY,statusENUM(PREPARED,COMMITTED,ROLLBACKED),create_timeDATETIME);// 协调者伪代码publicclassCoordinator{publicbooleanexecute(TransactionContextctx){// 阶段1准备for(Participantp:participants){if(!p.prepare(ctx))returnfalse;}// 阶段2提交try{for(Participantp:participants){p.commit(ctx);}returntrue;}catch(Exceptione){for(Participantp:participants){p.rollback(ctx);}returnfalse;}}}优缺点✅ 强一致性保证❌ 同步阻塞、协调者单点故障❌ 网络分区可能导致资源锁定二、补偿事务TCC三阶段操作阶段操作说明Try资源预留如冻结库存Confirm确认执行实际扣减Cancel补偿回滚释放冻结资源关键实现publicinterfacePaymentServiceTCC{TransactionalbooleantryPayment(LongorderId,BigDecimalamount);TransactionalbooleanconfirmPayment(LongorderId);TransactionalbooleancancelPayment(LongorderId);}注意事项幂等控制需处理重复调用空补偿处理未执行Try直接Cancel的情况悬挂问题防止Try超时后Cancel先执行三、本地消息表实现架构[业务事务] → [消息表记录] → [定时任务] → [MQ] → [消费者]核心代码CREATETABLElocal_message(idBIGINTAUTO_INCREMENTPRIMARYKEY,biz_idVARCHAR(64)NOTNULL,contentTEXTNOTNULL,statusTINYINTDEFAULT0COMMENT0-待发送,1-已发送,retry_countINTDEFAULT0,create_timeTIMESTAMPDEFAULTCURRENT_TIMESTAMP);TransactionalpublicvoidcreateOrder(Orderorder){// 1. 业务操作orderMapper.insert(order);// 2. 记录消息同事务LocalMessagemsgnewLocalMessage();msg.setBizId(order.getOrderNo());msg.setContent(JSON.toJSONString(order));messageMapper.insert(msg);}优势实现简单与业务解耦天然支持重试机制四、Saga模式执行模式对比类型特点协同式通过事件驱动协调编排式中央协调器控制流程状态机示例publicvoidplaceOrder(){try{// 正向操作inventoryService.reserveStock();paymentService.charge();shippingService.createShipment();}catch(Exceptione){// 逆向补偿shippingService.cancelShipment();paymentService.refund();inventoryService.releaseStock();}}日志追踪设计CREATETABLEsaga_log(saga_idVARCHAR(64)NOTNULL,step_nameVARCHAR(32)NOTNULL,statusVARCHAR(16)NOTNULL,paramsTEXT,create_timeTIMESTAMP,PRIMARYKEY(saga_id,step_name));方案选型指南对比矩阵方案一致性复杂度性能适用场景2PC强一致高低银行转账等强一致场景TCC最终一致中高中电商交易、支付系统本地消息表最终一致低高物流通知、积分系统Saga最终一致中中高长流程业务保险理赔组合方案推荐支付库存TCC 本地消息表订单履约Saga 异步补偿数据一致性2PC 超时补偿机制最佳实践原则业务分析根据CAP理论权衡一致性需求降级方案设计合理的补偿机制监控体系建立事务状态追踪看板压力测试验证方案在高并发下的表现注所有方案都需要配合幂等控制、重试机制和日志追踪才能保证可靠性本地消息表Local Message Table方案专业解析1. 核心设计思想本地消息表是一种基于最终一致性的分布式事务解决方案通过异步消息传递事务日志实现跨服务数据同步事务拆分将分布式事务拆分为多个本地事务消息驱动通过本地消息表记录待处理操作异步触发下游服务重试补偿定时任务保证消息可靠投递失败时自动重试2. 技术实现组件组件作用技术实现示例业务事务表记录主业务数据如订单表MySQLorder表本地消息表存储待分发的消息状态机模式MySQLlocal_message表定时任务调度器扫描未处理消息触发下游服务调用SpringScheduled 线程池幂等处理器防止下游服务重复消费Redis唯一ID/数据库唯一约束监控告警模块捕获长期失败消息触发人工干预Prometheus Grafana 企业微信报警3. 关键流程ACID特性保障ServiceBSchedulerLocalDBServiceAClientServiceBSchedulerLocalDBServiceAClientalt[调用成功][调用失败]loop[定时任务]创建订单开启事务INSERT INTO order (...) VALUES (...)INSERT INTO local_message (...) VALUES (...)提交事务返回成功SELECT * FROM local_message WHERE status0返回待处理消息HTTP POST /api/process200 OKUPDATE local_message SET status1UPDATE retry_countretry_count14. 技术关键点原子性Atomicity保障TransactionalpublicvoidcreateOrder(Orderorder){orderDao.insert(order);// 业务数据messageDao.insert(toMessage(order));// 事务消息}可靠性Durability设计消息表与业务表同库同实例利用RDBMS的WAL日志保证持久化定时任务采用至少一次at-least-once投递语义幂等性Idempotency控制PostMapping(/api/process)publicResponseprocess(RequestBodyMessagemsg){if(redis.setnx(msg.getId(),1,24h)){// 分布式锁realProcess(msg);// 真实业务逻辑}returnResponse.success();}一致性Consistency恢复longdelayMath.min(1000*Math.pow(2,retryCount),3600000);5. 生产级优化建议消息表分库分表CREATETABLElocal_message_${hash(id)%16}(...);批量消息处理Scheduled(fixedDelay5000)publicvoidbatchProcess(){ListMessagebatchmessageDao.scan(100);CompletableFuture[]futuresbatch.stream().map(msg-asyncProcess(msg)).toArray(CompletableFuture[]::new);CompletableFuture.allOf(futures).join();}死信队列处理if(msg.getRetryCount()MAX_RETRY){deadLetterQueue.add(msg);// 转入死信队列alarmService.notifyAdmin(msg);}6. 方案局限性时效性缺陷依赖定时任务扫描消息处理延迟通常在秒级架构约束要求业务消息必须可序列化存储维护成本需额外维护消息表、定时任务等组件7. 适用场景评估场景适用性理由订单创建→库存扣减★★★★★允许短暂延迟业务容忍最终一致支付成功→短信通知★★★★☆通知类操作对实时性要求较低金融账户转账★★☆☆☆需要强一致性建议使用TCC或Saga日志数据同步★★★★★天然适合异步处理该方案在电商、物流等互联网业务中广泛应用是平衡实现复杂度与可靠性的典型折中方案用外卖订餐理解本地消息表现实场景 vs 技术实现餐馆运营问题分布式系统问题解决方案前台接单记录业务数据存储MySQL订单表厨房小票事务消息local_message表服务员送小票消息投递定时任务扫描厨房小黑板幂等控制Redis唯一标识店长监督监控告警Prometheus钉钉核心四步流程1️⃣ 接单存双录事务原子性Transactional// 原子操作保证publicvoid接单(订单 order){// 记录主订单账本订单库.save(order);// 生成厨房小票消息小票机.save(new小票(order.id,新订单,LocalDateTime.now()));} 就像收银机按一次按钮同时打印顾客账单和厨房小票2️⃣ 异步送小票最终一致性每5分钟否是成功失败定时任务扫描未送小票是否超过3次?尝试送厨房放入死信队列标记已送达增加重试次数3️⃣ 厨房防重做幂等性def做菜(订单号):ifredis.get(订单号)处理中:return已在制作redis.set(订单号,处理中,ex3600)实际做菜操作()return开始制作‍ 相当于厨师长看到相同订单号会说“这份已经在炒了”4️⃣ 异常处理三板斧// 1. 指数退避重试Thread.sleep(1000*Math.pow(2,重试次数));// 2. 死信队列监控if(小票.重试次数3){钉钉报警(请店长处理订单小票.订单号);}// 3. 人工补偿入口PostMapping(/手动重试)publicString人工重试(String订单号){消息 msg小票机.find(订单号);厨房服务.做菜(msg.getContent());} 方案优势高可靠小票机相当于WAL日志断电也不丢单可扩展多个服务员消费者并行处理小票解耦合厨房装修服务升级不影响前台接单可追溯所有小票永久存档随时审计 注意事项小票内容要包含全部必要信息如顾客忌口厨房处理能力要匹配送单频率背压问题定期归档历史小票消息表分库策略 就像优秀的外卖系统订单可能稍有延迟但绝不会丢失或重复用这种模式可以处理订单→库存、支付→通知等大多数最终一致性场景。