分布式事务
什么时候使用分布式事务
分布式事务不是怎么选的问题,首先就是能不用就不用 实在要用那就是 一致性等级划分问题
你必须先问:
- 是否必须强一致? 如果必须跨服务强一致,需要 CP 模型下的分布式事务机制,而不是单纯 ACID。 CP 模型下的分布式事务,本质是通过同步协调协议(如 2PC 或共识算法)在多个节点之间达成一致提交决议。
单库事务(ACID) 2PC(Two-Phase Commit,两阶段提交)TCC(Try-Confirm-Cancel,尝试确认取消)
- 是否允许最终一致? 如果是最终一致 那么就可以使用 outbox 就是AP模型
- 是否涉及资金? 涉及资金 → 必须保证强一致或可控一致性。常见选择:1:单库设计(优先)2:TCC 3:余额中心独立服务
- 是否允许补偿? 可以用 Outbox ,不允许补偿:必须同步强一致,必须阻塞失败回滚,补偿能力决定一致性策略
- QPS 多少?
|
100
|
可以 2PC
|
|
1000
|
TCC 或 本地事务
|
|
10000+
|
必须异步最终一致
|
高并发 + 强一致 = 性能灾难。
什么是qps
假设:
- 1000 个用户 每分钟请求6次=6000 6000/60s=100qps QPS = 某段时间内的总请求数 / 时间(秒)
100 QPS × 单次事务耗时 = 并发事务数
第一步:估算单次事务耗时
假设:
- 本地事务 30ms
- 2PC 增加 2 次网络往返(RTT,Round Trip Time,网络往返时间)
- 每次 RTT = 10ms
- 总耗时 ≈ 30 + 2 × 10 = 50ms
第二步:计算并发事务数 公式: 并发数 ≈ QPS × 事务耗时(秒) 并发数 = 100 × 0.05 = 5个并发
mysql 的并发 可以调整到 1000+ 但是这只是并发请求 实际上的qps 是并发事务的响应时间
数据库并发通常有三层含义:
- 连接并发数(Connections,并发连接数) 一台服务器: 16 核 CPU 32GB 内存 典型 InnoDB 能承载: 2000+ 连接(但不都活跃)
- 活跃事务并发数(Active Transactions,活跃事务数) 并发能力 ≈ CPU 核心数 × 并行效率 一台服务器: 16 核 CPU 32GB 内存 典型 InnoDB 能承载: 200~500 活跃事务
- 真正执行 SQL 的并发线程数(Execution Concurrency,执行并发)
真正危险的不是高 QPS。 而是:长事务 × 热点数据
微服务并不一定需要分布式事务,关键取决于是否存在跨服务的强一致写操作。
在架构设计中,我会先做一致性等级划分:
- 资金类场景采用 TCC 保证强一致
- 单据流转类采用 Outbox 实现最终一致
- 非核心数据采用异步处理
而不是默认所有微服务都使用分布式事务。
目前订单和销账是同一个库,同一个系统 所以是属于本地事务由数据库保证ACID
单机事务 → ACID
分布式系统 → CAP 约束
工程妥协 → BASE 思想
具体实现 → 2PC / TCC / Outbox / Saga
|
字母
|
含义
|
核心关注点
|
|
A Atomicity
|
原子性
|
要么全成要么全失败
|
|
C Consistency
|
一致性
|
不违反业务规则
|
|
I Isolation
|
隔离性
|
并发不干扰
|
|
D Durability
|
持久性
|
提交后不丢
|
|
维度
|
2PC
|
TCC
|
Outbox Pattern
|
Saga
|
|
一致性模型
|
强一致(CP)
|
强一致(可控)
|
最终一致(BASE)
|
最终一致(BASE)
|
|
本质机制
|
数据库层两阶段提交
|
业务层预留资源
|
本地事务+可靠消息
|
本地事务+补偿事务
|
|
是否阻塞
|
是
|
否(数据库层)
|
否
|
否
|
|
是否需要补偿
|
否
|
是(Cancel)
|
重试机制
|
必须有补偿
|
|
锁持有时间
|
长
|
短
|
本地事务时间
|
本地事务时间
|
|
对业务侵入
|
低
|
高
|
低
|
中等
|
|
性能
|
低
|
中
|
高
|
高
|
|
可扩展性
|
差
|
中
|
好
|
好
|
|
实现复杂度
|
中
|
高
|
低
|
中高
|
|
适合高QPS
|
不适合
|
可支持
|
非常适合
|
适合
|
|
资金核心
|
小规模可用
|
推荐
|
不推荐
|
不推荐
|
|
是否中心协调
|
有(Coordinator)
|
无中心锁
|
无
|
取决于编排方式
|
|
常见框架
|
XA / Atomikos
|
Seata TCC
|
自研+MQ
|
Axon / Seata Saga
|
一、2PC(Two-Phase Commit) ✅ 优点 理论简单 强一致 数据库原生支持(XA 协议(分布式事务协议)) ❌ 缺点 阻塞严重 协调者单点 扩展性差 长事务风险高 🎯 适用场景 低 QPS 系统 内部系统 节点数量少 对一致性要求极高 🚫 不适用 高并发 互联网核心交易 微服务大规模拆分 二、TCC(Try-Confirm-Cancel) ✅ 优点 强一致 不依赖数据库全局锁 支持高并发 扩展性优于 2PC ❌ 缺点 侵入业务逻辑 开发复杂 必须保证幂等(Idempotent,幂等性) Cancel 设计困难 🎯 适用场景 资金扣减 账户余额变更 库存冻结 支付系统 🚫 不适用 简单 CRUD 低价值系统 三、Outbox Pattern ✅ 优点 简单 性能好 无全局锁 不影响数据库并发能力 ❌ 缺点 最终一致 需要重试机制 需要消息幂等 🎯 适用场景 订单创建后发积分 用户注册后发通知 数据同步 统计系统 🚫 不适用 实时资金强一致 必须秒级对账一致的场景 四、Saga ✅ 优点 适合长事务 不阻塞 易扩展 ❌ 缺点 补偿逻辑复杂 数据可能短时间不一致 业务侵入明显 🎯 适用场景 电商完整下单流程 跨多个微服务 需要事务编排 🚫 不适用 实时账务系统 高精度金融核心
cap举例
CAP 定理
- C(Consistency) 一致性
- A(Availability)原子性
- P(Partition tolerance)
四、为什么必须二选一? 假设场景: 两个节点: Node A Node B 账户余额 = 100 突然网络断开(分区发生) A 和 B 无法通信。 此时发生写请求: 用户在 Node A 扣减 30。 A 变成 70。 现在问题来了: 用户去 Node B 查询余额。 你有两个选择: 选择 1:保证强一致 Node B 不能确认 A 是否已更新。 为了保证一致: 👉 B 必须拒绝请求或等待网络恢复。 结果: 查询失败 服务不可用 你保住了一致性 但牺牲了可用性 这是 CP。 选择 2:保证可用性 Node B 直接返回当前值 100。 结果: 用户看到旧数据 数据不一致 但系统可用。 这是 AP。 五、这就是为什么必须二选一 因为: 分区发生时,节点之间无法同步。 你无法同时做到: 所有节点数据一致 所有节点都响应请求 这是一个数学证明结论。 不是工程选择问题。
mvcc 是要是适用了innodb存储引擎 那么会自动实现
什么是乐观锁和状态机
拿销账举例 销账更新金额的时候 读不进行租塞,写的时候进行判断 所以可以在表中增加版本号,因为销账有流程为了保证更安全还可以加上状态机 就是待处理只可以变成处理中
ef虽然实现了乐观锁 只需要加上特性就好 但是性能有问题 因为1000条他就会生产1000个update语句
XA 事务不是 .NET 特有的事务机制,它是数据库层面支持的分布式事务协议。
在 .NET 中如果使用 TransactionScope 并跨多个数据库连接,可能会触发 MSDTC,而 MSDTC 底层可能会使用数据库的 XA 实现两阶段提交。
一、三者本质定位
|
方案
|
本质类型
|
控制层级
|
|
2PC(XA)
|
数据库层分布式事务
|
资源管理器
|
|
TCC
|
业务层两阶段提交
|
应用层
|
|
Outbox
|
本地事务 + 可靠消息
|
架构层
|
一句话总结:
- 2PC:让数据库帮你做分布式事务
- TCC:让业务帮你做分布式事务
- Outbox:避免分布式事务
二、分别解决了什么问题?
一、2PC(Two-Phase Commit)
解决的问题
多个数据库之间如何保证原子提交?
典型场景:
- 跨库更新
- 跨数据源事务
工作机制
阶段 1:Prepare
协调者询问所有参与者是否可以提交
阶段 2:Commit / Rollback
优点
- 强一致
- 实现简单(框架支持)
- 对业务无侵入
缺点
- 同步阻塞
- 锁时间长
- 协调者单点
- 性能差
- 容易雪崩
适用场景
- 银行核心账务
- 并发不高
- 强一致优先
- 内网系统
互联网系统几乎不用。
二、TCC(Try-Confirm-Cancel)
解决的问题
在高并发环境下,如何保证强一致,同时避免数据库长锁?
工作机制
Try:预留资源 (冻结金额)
Confirm:正式提交(扣减金额)
Cancel:回滚补偿(解冻金额)
本质:业务层 2PC
优点
- 强一致
- 不依赖数据库锁
- 并发能力高于 XA
缺点
- 侵入性强
- 需要三套接口
- 要处理空回滚、悬挂问题
- 必须幂等
- 实现复杂
适用场景
- 账户余额扣减
- 冻结资金
- 支付系统
- 金融级强一致场景
财务系统“钱”的场景。
三、Outbox Pattern
解决的问题
本地事务成功,但消息发送失败导致数据不一致
核心:
本地事务写:
- 业务数据
- 消息记录
异步发送。
优点
- 高性能
- 无分布式锁
- 架构简单
- 互联网主流方案
缺点
- 最终一致
- 需要幂等
- 需要补偿机制
- Outbox 表膨胀问题
适用场景
- 订单状态流转
- 业务事件通知
- 电商系统
- 高并发系统
财务系统“单据流转”的场景。
三、架构级对比表
|
对比维度
|
2PC
|
TCC
|
Outbox
|
|
一致性
|
强一致
|
强一致
|
最终一致
|
|
锁资源
|
数据库锁
|
业务冻结
|
无锁
|
|
并发能力
|
低
|
中
|
高
|
|
侵入性
|
低
|
高
|
低
|
|
实现复杂度
|
低
|
高
|
中
|
|
是否阻塞
|
是
|
部分
|
否
|
|
是否适合互联网
|
不适合
|
部分场景
|
非常适合
|
四、从“解决问题的思路”角度理解
2PC 思路:
把多个数据库当成一个数据库
TCC 思路:
不依赖数据库锁,用业务状态控制一致性
Outbox 思路:
不做分布式事务,用可靠消息保证最终一致
五、财务系统里的正确分级设计
|
场景
|
推荐方案
|
|
账户余额扣减
|
TCC
|
|
销账单生成
|
Outbox
|
|
订单状态流转
|
Outbox
|
|
跨库强一致账务
|
2PC(极少)
|
六、面试高级回答模板
你可以这样回答:
2PC 是数据库层分布式事务方案,保证强一致,但性能较低,适用于并发不高的金融核心系统。
TCC 是业务层两阶段提交,通过冻结资源实现强一致,适合资金类场景,但侵入性较高。
Outbox 是通过本地事务写消息表,再异步发送消息实现最终一致,是互联网系统主流方案,适合高并发业务场景。
在实际架构中,应根据一致性等级分级设计,而不是一刀切。
七、真正架构师的思考方式
本文来自博客园,作者:12不懂3,转载请注明原文链接:https://www.cnblogs.com/LZXX/p/19673666

浙公网安备 33010602011771号