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

0 阅读3分钟

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

下单 → 扣库存 → 扣余额 → 发货

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

  • 数据库事务失效了

  • BEGIN/COMMIT 再也管不住全局

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

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

在单体应用中:

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

在微服务中:

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

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

现实系统里:

  • P必须要

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

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

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

2. 流程图

BEGIN
  插入订单
  插入消息表(pending)
COMMIT

后台任务:
  扫描 pending 消息
  发送消息
  成功 → 标记 done

3. 示例

type Order struct {
	ID     int64
	Amount int64
}

type Message struct {
	ID      int64
	Topic   string
	Payload string
	Status  string // pending / done
}
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()
}

4. 优缺点

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

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

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

三、事务消息(RocketMQ 思路)

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

2. 三阶段

1. 发送 Half Message(MQ 不投递)
2. 执行本地事务
3. Commit / Rollback 消息

3. 示例

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
}

4. 优缺点

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

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

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

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

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

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

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

2. 示例流程

T1: 创建订单        → C1: 取消订单
T2: 扣库存          → C2: 回滚库存
T3: 扣余额          → C3: 回滚余额

3. 示例

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
}

4. 优缺点

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

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

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

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

1. 三个阶段

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

2. 示例

func TryDeduct(stock int) error {
// 冻结库存
return nil
}

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

func CancelDeduct(stock int) error {
// 释放冻结库存
return nil
}

3. 优缺点

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

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

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

六、四种方案对比

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

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


*源码地址*

1、公众号“Codee君”回复“源码”获取源码

2、pan.baidu.com/s/1B6pgLWfS…


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