每日一 Go-72、分布式事务 & 一致性:本地消息表、事务消息、SAGA、TCC怎么选?

单机事务靠数据库,分布式事务靠"妥协 + 设计"。比如一个经典的业务

js 复制代码
下单 → 扣库存 → 扣余额 → 发货

拆成多个服务后,会有以下问题:

  • 数据库事务失效了

  • BEGIN/COMMIT 再也管不住全局

  • 网络、宕机、重试,任何一步都可能出错。

一、为什么分布式事务这么难?

在单体应用中:

java 复制代码
一个进程 + 一个数据库 = ACID
ACID = Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)

在微服务中:

java 复制代码
多个服务 + 多个数据库 + 网络 = Chaos
Chaos = 系统不再是确定性的,而是充满不可预测失败的状态空间

CAP定理告诉我们:分布式系统中,一致性(C)、可用性(A)、分区容错(P)不可兼得。

现实系统里:

  • P必须要

  • 大多数业务会在C和A之间权衡

二、本地消息表(最常用,最稳)

  1. 思想:业务操作+记录消息,在同一个本地事务里完成;然后:异步任务扫描消息表,发送 MQ/HTTP,成功后标记已发送。

  2. 流程图

sql 复制代码
BEGIN
  插入订单
  插入消息表(pending)
COMMIT

后台任务:
  扫描 pending 消息
  发送消息
  成功 → 标记 done
  1. 示例
go 复制代码
type Order struct {
	ID     int64
	Amount int64
}

type Message struct {
	ID      int64
	Topic   string
	Payload string
	Status  string // pending / done
}
go 复制代码
func CreateOrder(db *sql.DB, amount int64) error {
	tx, err := db.Begin()
if err != nil {
return err
	}

// 1. 创建订单
	_, err = tx.Exec(
"insert into orders(amount) values(?)",
		amount,
	)
if err != nil {
		tx.Rollback()
return err
	}

// 2. 写本地消息表
	_, err = tx.Exec(
"insert into messages(topic, payload, status) values(?, ?, ?)",
"order_created",
"{...}",
"pending",
	)
if err != nil {
		tx.Rollback()
return err
	}

return tx.Commit()
}
  1. 优缺点

优点:简单;不依赖MQ;极其稳定。

缺点:有延迟;要自己写消息投递和重试逻辑。

适合业务:订单、支付、通知类业务。

三、事务消息(RocketMQ 思路)

  1. 核心思想:先发"半消息",再提交本地事务,最后确认消息。

  2. 三阶段

markdown 复制代码
1. 发送 Half Message(MQ 不投递)
2. 执行本地事务
3. Commit / Rollback 消息
  1. 示例
go 复制代码
func SendTxMessage() error {
// 1. 发送半消息
	msgID := mq.SendHalf("order_created", payload)

// 2. 执行本地事务
	err := createOrder()
if err != nil {
		mq.Rollback(msgID)
return err
	}

// 3. 提交消息
	mq.Commit(msgID)
return nil
}
  1. 优缺点

优点:消息与业务强绑定;不需要扫描表。

缺点:依赖MQ;心智成本高。

适合业务:已经深度使用RocketMQ的系统。

心智成本 = 一个系统对"人"的复杂度

包括:学习成本、理解成本、使用成本、维护成本、排错成本、接手成本

四、SAGA:长事务的现实解法

  1. 核心思想:把一个大事务,拆成多个本地事务 + 对应的补偿操作

  2. 示例流程

http 复制代码
T1: 创建订单        → C1: 取消订单
T2: 扣库存          → C2: 回滚库存
T3: 扣余额          → C3: 回滚余额
  1. 示例
go 复制代码
func SagaOrder() error {
if err := createOrder(); err != nil {
return err
	}

if err := deductStock(); err != nil {
		cancelOrder()
return err
	}

if err := deductBalance(); err != nil {
		restoreStock()
		cancelOrder()
return err
	}

return nil
}
  1. 优缺点

优点:不需要锁;可扩展性好;非常适合微服务。

缺点:补偿逻辑复杂;中间态暴露。

适合业务:电商、长流程业务

五、TCC:最"强"的分布式事务

  1. 三个阶段

|---------|------|
| 阶段 | 含义 |
| Try | 预留资源 |
| Confirm | 真正提交 |
| Cancel | 回滚释放 |

  1. 示例
go 复制代码
func TryDeduct(stock int) error {
// 冻结库存
return nil
}

func ConfirmDeduct(stock int) error {
// 扣减冻结库存
return nil
}

func CancelDeduct(stock int) error {
// 释放冻结库存
return nil
}
  1. 优缺点

优点:接近强一致;状态可控。

缺点:代码量大;对业务入侵极强。

适合业务:资金、交易、金融系统。

六、四种方案对比

|-------|------|-------|--------|
| 方案 | 一致性 | 复杂度 | 常见场景 |
| 本地消息表 | 最终一致 | ⭐⭐ | 订单、通知 |
| 事务消息 | 最终一致 | ⭐⭐⭐ | MQ驱动系统 |
| SAGA | 最终一致 | ⭐⭐⭐ | 长流程 |
| TCC | 强一致 | ⭐⭐⭐⭐⭐ | 金融 |


友情链接:加班费计算器(vx小程序搜索"加班计")


*源码地址*

评论区给


如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!

相关推荐
科技互联.1 小时前
2026轻量化图形引擎白皮书:PG官网发布渠道与分布式PG数据库架构解析
数据库·分布式·数据库架构
sycmancia1 小时前
Qt——程序中的配置文件
开发语言·qt
赶在日落之前1 小时前
使用conda-pack打包完整 Python 环境 + 依赖包,传到无网机器解压即用
开发语言·人工智能·python
段一凡-华北理工大学1 小时前
工业领域的Hadoop架构学习~系列文章10:数据序列化与压缩
大数据·人工智能·hadoop·分布式·学习·工业智能体·高炉炼铁智能化
雨落在了我的手上1 小时前
Java数据结构(一):初识集合框架
java·开发语言
程序大视界1 小时前
【C++ 从基础到项目实战】C++(三):函数进阶——重载、回调、递归与默认参数
开发语言·c++·cpp
爱喝水的鱼丶1 小时前
SAP-ABAP:SAP 简单报表输出开发系列(共6篇)第二篇:SAP 报表数据筛选优化:选择屏幕自定义与查询效率提升
开发语言·数据库·学习·性能优化·sap·abap
HappyAcmen1 小时前
8.角色 Prompt 模板
开发语言·python·prompt
oort1231 小时前
VLStream 全开源决策式 AI 视频平台 技术视角完整说明
大数据·开发语言·人工智能·经验分享·python·开源·音视频