单机事务靠数据库,分布式事务靠"妥协 + 设计"。比如一个经典的业务
js
下单 → 扣库存 → 扣余额 → 发货
拆成多个服务后,会有以下问题:
-
数据库事务失效了
-
BEGIN/COMMIT 再也管不住全局
-
网络、宕机、重试,任何一步都可能出错。
一、为什么分布式事务这么难?
在单体应用中:
java
一个进程 + 一个数据库 = ACID
ACID = Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)
在微服务中:
java
多个服务 + 多个数据库 + 网络 = Chaos
Chaos = 系统不再是确定性的,而是充满不可预测失败的状态空间
CAP定理告诉我们:分布式系统中,一致性(C)、可用性(A)、分区容错(P)不可兼得。
现实系统里:
-
P必须要
-
大多数业务会在C和A之间权衡
二、本地消息表(最常用,最稳)
-
思想:业务操作+记录消息,在同一个本地事务里完成;然后:异步任务扫描消息表,发送 MQ/HTTP,成功后标记已发送。
-
流程图
sql
BEGIN
插入订单
插入消息表(pending)
COMMIT
后台任务:
扫描 pending 消息
发送消息
成功 → 标记 done
- 示例
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()
}
- 优缺点
优点:简单;不依赖MQ;极其稳定。
缺点:有延迟;要自己写消息投递和重试逻辑。
适合业务:订单、支付、通知类业务。
三、事务消息(RocketMQ 思路)
-
核心思想:先发"半消息",再提交本地事务,最后确认消息。
-
三阶段
markdown
1. 发送 Half Message(MQ 不投递)
2. 执行本地事务
3. Commit / Rollback 消息
- 示例
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
}
- 优缺点
优点:消息与业务强绑定;不需要扫描表。
缺点:依赖MQ;心智成本高。
适合业务:已经深度使用RocketMQ的系统。
心智成本 = 一个系统对"人"的复杂度
包括:学习成本、理解成本、使用成本、维护成本、排错成本、接手成本
四、SAGA:长事务的现实解法
-
核心思想:把一个大事务,拆成多个本地事务 + 对应的补偿操作
-
示例流程
http
T1: 创建订单 → C1: 取消订单
T2: 扣库存 → C2: 回滚库存
T3: 扣余额 → C3: 回滚余额
- 示例
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
}
- 优缺点
优点:不需要锁;可扩展性好;非常适合微服务。
缺点:补偿逻辑复杂;中间态暴露。
适合业务:电商、长流程业务
五、TCC:最"强"的分布式事务
- 三个阶段
|---------|------|
| 阶段 | 含义 |
| Try | 预留资源 |
| Confirm | 真正提交 |
| Cancel | 回滚释放 |
- 示例
go
func TryDeduct(stock int) error {
// 冻结库存
return nil
}
func ConfirmDeduct(stock int) error {
// 扣减冻结库存
return nil
}
func CancelDeduct(stock int) error {
// 释放冻结库存
return nil
}
- 优缺点
优点:接近强一致;状态可控。
缺点:代码量大;对业务入侵极强。
适合业务:资金、交易、金融系统。
六、四种方案对比
|-------|------|-------|--------|
| 方案 | 一致性 | 复杂度 | 常见场景 |
| 本地消息表 | 最终一致 | ⭐⭐ | 订单、通知 |
| 事务消息 | 最终一致 | ⭐⭐⭐ | MQ驱动系统 |
| SAGA | 最终一致 | ⭐⭐⭐ | 长流程 |
| TCC | 强一致 | ⭐⭐⭐⭐⭐ | 金融 |
友情链接:加班费计算器(vx小程序搜索"加班计")
*源码地址*
评论区给
如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!