分布式事务的一致性问题

1 场景

  • 服务1 调用 服务2 的接口(比如 HTTP / gRPC);

  • 服务2 修改自己的数据并返回成功;

  • 服务1 收到服务2返回结果后,继续修改自己数据库;

  • ❌ 如果服务1 此时数据库操作失败(例如网络、SQL异常等),就会导致:

    • 服务2 的数据已经修改;

    • 服务1 的数据没改成功;

    • 出现服务间数据不一致问题

2 解决方案

1. 本地事务 + 可靠消息最终一致性(推荐)

流程如下:

  • 服务1先修改本地数据,把需要通知服务2的消息写入“消息表”(或发送消息到MQ)

  • 提交本地事务;

  • 然后异步发送消息给服务2(由消息中间件或定时任务发出);

  • 服务2收到消息后修改自己的数据;

  • 若失败可重试、补偿,保证最终一致性。

📌 典型架构:微服务 + 消息队列(如 RabbitMQ、Kafka、RocketMQ)

✅ 优点:无强依赖,易落地
⚠️ 缺点:不是强一致,而是最终一致性

2. 分布式事务协议(SAGA 长事务 补偿操作)

SAGA 模式:

假如你用一个 SAGA 引擎(比如 DTM、Seata),你会定义事务步骤和补偿接口:

[{ "action": "/create_order", "compensate": "/cancel_order" },
{ "action": "/reduce_inventory", "compensate": "/restore_inventory" },
{ "action": "/deduct_balance", "compensate": "/refund_balance" }
]

框架自动控制流程:

  • 如果 deduct_balance 失败了,自动倒序执行:

    • refund_balance → restore_inventory → cancel_order

  • 如果 restore_inventory 失败了,也可以重试;

  • 整个事务状态持久化,有日志、有可视化界面

基于 DTM(分布式事务管理器)实现 SAGA 模式的实际例子,使用 Flask + Python

前提准备 安装 DTM:

你可以用 Docker 启动:

docker run -d --name dtm \ -p 36789:36789 -p 8080:8080 \ yedf/dtm
  • 36789 是 gRPC 端口(不需要可以忽略)

  • 8080 是 HTTP 事务管理接口

安装 Python 客户端:

pip install dtmcli
from dtmcli.saga import Saga

dtm_url = "http://localhost:36789/api/dtmsvr"
gid = "saga_transaction_001"

saga = Saga(dtm_url, gid)

saga.add(
    action="http://localhost:5001/order/create",
    compensate="http://localhost:5001/order/cancel"
).add(
    action="http://localhost:5002/inventory/deduct",
    compensate="http://localhost:5002/inventory/restore"
).add(
    action="http://localhost:5003/account/deduct",
    compensate="http://localhost:5003/account/refund"
)

saga.submit()
print("✅ SAGA事务提交成功,GID:", gid)

python run_saga.py  查看控制台输出,你会看到:

  • 如果所有步骤都成功:打印三个 ✅ 操作

  • 如果某一步失败(如注释掉余额操作里的失败),你会看到自动执行补偿逻辑(取消订单、恢复库存、退款)

3. 写反向补偿逻辑(手动回滚)

  • 服务1在失败后,尝试调用服务2进行“撤销”操作;

  • 例如删除、恢复原状态;

  • 必须要求服务2提供回滚接口;

 

posted @ 2025-08-05 09:48  Mr沈  阅读(26)  评论(0)    收藏  举报