返回文章列表
技术2026年9月18日23 分钟阅读

分布式事务:从2PC到Saga的事务模式演进与工程实践

分布式事务:从2PC到Saga的事务模式演进与工程实践

一、理论基础:ACID、CAP与BASE

1.1 单机事务的ACID保证

在单机数据库中,事务的ACID特性由数据库引擎保证:

原子性(Atomicity):

事务中的所有操作要么全部完成,要么全部不完成。

实现机制:

  • Undo Log:回滚时恢复数据
  • Redo Log:提交后确保持久化
事务执行流程:

Begin
  │
  ▼
写 Undo Log  <── 记录修改前的值
  │
  ▼
修改数据
  │
  ▼
写 Redo Log  <── 记录修改后的值
  │
  ▼
Commit
  │
  ▼
Flush Redo Log to Disk

一致性(Consistency):

事务执行前后,数据库从一个一致状态转换到另一个一致状态。

注意:这里的一致性指数据完整性约束,而非分布式一致性。

隔离性(Isolation):

并发事务之间相互隔离,不会互相干扰。

隔离级别:

隔离级别 脏读 不可重复读 幻读 实现机制
Read Uncommitted ✓ ✓ ✓ 无
Read Committed ✗ ✓ ✓ MVCC
Repeatable Read ✗ ✗ ✓ MVCC + 间隙锁
Serializable ✗ ✗ ✗ 锁 + MVCC

持久性(Durability):

已提交的事务,其修改永久保存。

实现机制:

  • Write-Ahead Logging (WAL)
  • 强制刷盘(fsync)

1.2 CAP定理:分布式系统的三元悖论

2000年,Eric Brewer 提出 CAP定理:

在分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)三者不可兼得,最多只能同时满足两项。

形式化表述:

对于分布式系统中的任意请求:

  • C(一致性):所有节点看到相同的数据
  • A(可用性):每个请求都能获得响应(不保证最新)
  • P(分区容错性):网络分区时系统仍能运行

证明思路:

假设系统同时满足 C、A、P:

1. 网络发生分区,将系统分为 G1 和 G2
2. 客户端向 G1 写入 v1
3. 为保证 A,G1 必须响应成功
4. 客户端从 G2 读取
5. 为保证 C,G2 必须返回 v1
6. 但 G1 和 G2 无法通信(分区),G2 不可能知道 v1
7. 矛盾!

结论:P 必须满足(网络不可靠),因此 C 和 A 只能选其一。

实际系统的选择:

系统类型 选择 代表
传统数据库 CP MySQL、PostgreSQL
NoSQL AP Cassandra、DynamoDB
分布式KV 可配置 etcd (CP)、Eureka (AP)

1.3 BASE理论:最终一致性的实践

eBay 架构师 Dan Pritchett 提出 BASE 作为 ACID 的替代:

  • Basically Available(基本可用):系统出现故障时,允许损失部分可用性
  • Soft state(软状态):允许系统中的数据存在中间状态
  • Eventually consistent(最终一致性):不保证实时一致性,但保证最终一致

BASE vs ACID:

特性 ACID BASE
一致性 强一致性 最终一致性
可用性 优先保证一致性 优先保证可用性
性能 较低(锁开销) 较高(无锁)
复杂度 数据库保证 应用层处理
适用场景 金融交易 社交网络、电商

二、两阶段提交(2PC):强一致性的尝试

2.1 2PC协议概述

两阶段提交(Two-Phase Commit) 是实现分布式事务的经典协议,由协调者(Coordinator)和参与者(Participants)组成。

角色定义:

2PC 角色:

协调者(Coordinator):
├── 接收客户端事务请求
├── 向所有参与者发送准备请求
├── 根据参与者响应决定提交或回滚
└── 向参与者发送最终决定

参与者(Participant):
├── 执行本地事务操作
├── 向协调者报告准备状态
├── 根据协调者决定提交或回滚
└── 持久化事务状态(用于恢复)

2.2 协议流程

Phase 1:准备阶段(Prepare Phase)

Coordinator              Participants
    │                         │
    │  1. Prepare Request     │
    │ ───────────────────────>│
    │  (包含事务内容)          │
    │                         │
    │  2. 执行本地事务          │
    │  3. 写 Prepare Log       │
    │  4. 锁定资源             │
    │                         │
    │  5. Yes/No Response     │
    │ <───────────────────────│
    │  (准备就绪或失败)         │

Phase 2:提交阶段(Commit Phase)

Coordinator              Participants
    │                         │
    │  6. Commit/Abort        │
    │ ───────────────────────>│
    │  (基于所有响应)          │
    │                         │
    │  7. 提交或回滚本地事务     │
    │  8. 释放锁               │
    │  9. 写 Commit/Abort Log │
    │                         │
    │  10. ACK                │
    │ <───────────────────────│
    │                         │
    │  11. 完成事务            │

2.3 Python实现

python
import threading
import time
from enum import Enum
from typing import List, Dict, Optional
from dataclasses import dataclass

class TransactionStatus(Enum):
    PENDING = "pending"
    PREPARING = "preparing"
    PREPARED = "prepared"
    COMMITTING = "committing"
    COMMITTED = "committed"
    ABORTING = "aborting"
    ABORTED = "aborted"

@dataclass
class TransactionRecord:
    tx_id: str
    status: TransactionStatus
    participants: List[str]
    responses: Dict[str, bool]

class TwoPhaseCommitCoordinator:
    """
    2PC 协调者实现
    """
    
    def __init__(self, coordinator_id: str):
        self.coordinator_id = coordinator_id
        self.participants: List[TwoPhaseCommitParticipant] = []
        self.transactions: Dict[str, TransactionRecord] = {}
        self.lock = threading.RLock()
    
    def register_participant(self, participant):
        """注册参与者"""
        self.participants.append(participant)
    
    def execute_transaction(self, tx_id: str, operations: Dict[str, any]) -> bool:
        """
        执行两阶段提交事务
        
        Args:
            tx_id: 事务ID
            operations: {participant_id: operation}
        
        Returns:
            True: 提交成功
            False: 回滚
        """
        with self.lock:
            # 初始化事务记录
            tx_record = TransactionRecord(
                tx_id=tx_id,
                status=TransactionStatus.PREPARING,
                participants=list(operations.keys()),
                responses={}
            )
            self.transactions[tx_id] = tx_record
        
        # Phase 1: 准备阶段
        print(f"[Coordinator] 事务 {tx_id} 开始准备阶段")
        
        prepare_results = {}
        for participant in self.participants:
            if participant.participant_id in operations:
                operation = operations[participant.participant_id]
                try:
                    result = participant.prepare(tx_id, operation)
                    prepare_results[participant.participant_id] = result
                    print(f"[Coordinator] {participant.participant_id} 准备{'成功' if result else '失败'}")
                except Exception as e:
                    prepare_results[participant.participant_id] = False
                    print(f"[Coordinator] {participant.participant_id} 准备异常: {e}")
        
        # 检查所有参与者是否都准备成功
        all_prepared = all(prepare_results.values())
        
        # Phase 2: 提交阶段
        if all_prepared:
            print(f"[Coordinator] 所有参与者准备成功,开始提交")
            return self._commit_transaction(tx_id, prepare_results)
        else:
            print(f"[Coordinator] 部分参与者准备失败,开始回滚")
            return self._abort_transaction(tx_id, prepare_results)
    
    def _commit_transaction(self, tx_id: str, participants: Dict[str, bool]) -> bool:
        """提交事务"""
        tx_record = self.transactions[tx_id]
        tx_record.status = TransactionStatus.COMMITTING
        
        # 写 Commit Log(持久化)
        self._persist_commit_log(tx_id)
        
        # 向所有参与者发送 Commit 指令
        for participant in self.participants:
            if participant.participant_id in participants:
                try:
                    participant.commit(tx_id)
                    print(f"[Coordinator] {participant.participant_id} 提交成功")
                except Exception as e:
                    # 记录异常,需要后续补偿
                    print(f"[Coordinator] {participant.participant_id} 提交异常: {e}")
        
        tx_record.status = TransactionStatus.COMMITTED
        return True
    
    def _abort_transaction(self, tx_id: str, participants: Dict[str, bool]) -> bool:
        """回滚事务"""
        tx_record = self.transactions[tx_id]
        tx_record.status = TransactionStatus.ABORTING
        
        # 写 Abort Log(持久化)
        self._persist_abort_log(tx_id)
        
        # 向已准备的参与者发送 Abort 指令
        for participant_id, prepared in participants.items():
            if prepared:  # 只回滚已准备的参与者
                for participant in self.participants:
                    if participant.participant_id == participant_id:
                        try:
                            participant.abort(tx_id)
                            print(f"[Coordinator] {participant_id} 回滚成功")
                        except Exception as e:
                            print(f"[Coordinator] {participant_id} 回滚异常: {e}")
        
        tx_record.status = TransactionStatus.ABORTED
        return False
    
    def _persist_commit_log(self, tx_id: str):
        """持久化提交日志"""
        # 实际实现需要写入 WAL
        print(f"[Coordinator] 持久化 Commit Log: {tx_id}")
    
    def _persist_abort_log(self, tx_id: str):
        """持久化回滚日志"""
        print(f"[Coordinator] 持久化 Abort Log: {tx_id}")


class TwoPhaseCommitParticipant:
    """
    2PC 参与者实现
    """
    
    def __init__(self, participant_id: str):
        self.participant_id = participant_id
        self.prepared_transactions: Dict[str, any] = {}
        self.committed_transactions: set = set()
        self.aborted_transactions: set = set()
        self.lock = threading.RLock()
    
    def prepare(self, tx_id: str, operation: any) -> bool:
        """
        准备阶段
        
        执行本地事务但不提交,记录 Undo/Redo Log
        """
        with self.lock:
            print(f"[{self.participant_id}] 准备事务 {tx_id}: {operation}")
            
            try:
                # 1. 执行本地操作(但不提交)
                result = self._execute_local(operation)
                
                # 2. 写 Prepare Log(持久化)
                self._write_prepare_log(tx_id, operation)
                
                # 3. 记录事务状态
                self.prepared_transactions[tx_id] = {
                    'operation': operation,
                    'result': result,
                    'undo_log': self._generate_undo_log(operation)
                }
                
                return True
            except Exception as e:
                print(f"[{self.participant_id}] 准备失败: {e}")
                return False
    
    def commit(self, tx_id: str):
        """提交本地事务"""
        with self.lock:
            if tx_id not in self.prepared_transactions:
                raise ValueError(f"事务 {tx_id} 未准备")
            
            print(f"[{self.participant_id}] 提交事务 {tx_id}")
            
            # 1. 提交本地事务
            self._commit_local(tx_id)
            
            # 2. 写 Commit Log
            self._write_commit_log(tx_id)
            
            # 3. 清理
            del self.prepared_transactions[tx_id]
            self.committed_transactions.add(tx_id)
    
    def abort(self, tx_id: str):
        """回滚本地事务"""
        with self.lock:
            if tx_id not in self.prepared_transactions:
                return  # 可能已经回滚或从未准备
            
            print(f"[{self.participant_id}] 回滚事务 {tx_id}")
            
            # 1. 使用 Undo Log 回滚
            undo_log = self.prepared_transactions[tx_id]['undo_log']
            self._apply_undo_log(undo_log)
            
            # 2. 写 Abort Log
            self._write_abort_log(tx_id)
            
            # 3. 清理
            del self.prepared_transactions[tx_id]
            self.aborted_transactions.add(tx_id)
    
    def _execute_local(self, operation: any) -> any:
        """执行本地操作(模拟)"""
        print(f"[{self.participant_id}] 执行: {operation}")
        return f"result_of_{operation}"
    
    def _generate_undo_log(self, operation: any) -> any:
        """生成 Undo Log"""
        return f"undo_for_{operation}"
    
    def _commit_local(self, tx_id: str):
        """提交本地事务"""
        print(f"[{self.participant_id}] 本地提交")
    
    def _apply_undo_log(self, undo_log: any):
        """应用 Undo Log"""
        print(f"[{self.participant_id}] 应用 Undo Log: {undo_log}")
    
    def _write_prepare_log(self, tx_id: str, operation: any):
        """写 Prepare Log"""
        print(f"[{self.participant_id}] 写 Prepare Log")
    
    def _write_commit_log(self, tx_id: str):
        """写 Commit Log"""
        print(f"[{self.participant_id}] 写 Commit Log")
    
    def _write_abort_log(self, tx_id: str):
        """写 Abort Log"""
        print(f"[{self.participant_id}] 写 Abort Log")


# 使用示例
def demo_2pc():
    # 创建协调者
    coordinator = TwoPhaseCommitCoordinator("coord-1")
    
    # 创建参与者
    participant_a = TwoPhaseCommitParticipant("service-a")
    participant_b = TwoPhaseCommitParticipant("service-b")
    participant_c = TwoPhaseCommitParticipant("service-c")
    
    # 注册参与者
    coordinator.register_participant(participant_a)
    coordinator.register_participant(participant_b)
    coordinator.register_participant(participant_c)
    
    # 执行事务
    operations = {
        "service-a": "deduct_inventory",
        "service-b": "create_order",
        "service-c": "deduct_balance"
    }
    
    success = coordinator.execute_transaction("tx-001", operations)
    print(f"\n事务结果: {'提交' if success else '回滚'}")

# demo_2pc()

2.4 2PC的问题与局限

同步阻塞问题:

参与者必须等待协调者的最终决定:
- 准备成功后,参与者持有锁
- 如果协调者故障,参与者无限等待
- 其他事务无法访问被锁定的资源

解决方案:超时机制 + 人工介入

单点故障:

协调者是单点:
- 协调者故障,整个事务挂起
- 需要协调者恢复或人工决策

解决方案:协调者高可用(Paxos/Raft)

数据不一致风险:

场景:协调者在发送 Commit 后崩溃
- 部分参与者收到 Commit
- 部分参与者未收到 Commit
- 需要参与者之间的协调

解决方案:3PC 或 Saga 模式

三、三阶段提交(3PC):2PC的改进

3.1 3PC的设计目标

3PC(Three-Phase Commit) 在 2PC 的基础上增加了预提交阶段,减少阻塞时间。

核心改进:

  • 引入超时机制
  • 协调者和参与者都可以超时继续
  • 减少同步阻塞

3.2 协议流程

Phase 1:CanCommit

Coordinator -> Participants: CanCommit?
Participants -> Coordinator: Yes/No

目的:检查参与者是否可以执行事务
不执行实际操作,不锁定资源

Phase 2:PreCommit

如果所有参与者返回 Yes:
  Coordinator -> Participants: PreCommit
  Participants: 执行本地操作,写 Prepare Log,锁定资源
  Participants -> Coordinator: ACK

如果有参与者返回 No:
  Coordinator -> Participants: Abort

Phase 3:DoCommit

Coordinator -> Participants: DoCommit / Abort
Participants: 提交或回滚,释放锁

3.3 超时机制

python
class ThreePhaseCommitCoordinator:
    """
    3PC 协调者实现(简化版)
    """
    
    def __init__(self, timeout: float = 5.0):
        self.timeout = timeout
    
    def execute_transaction(self, tx_id: str, operations: Dict) -> bool:
        # Phase 1: CanCommit
        can_commit_results = self._can_commit_phase(tx_id, operations)
        if not all(can_commit_results.values()):
            self._abort(tx_id)
            return False
        
        # Phase 2: PreCommit
        pre_commit_results = self._pre_commit_phase(tx_id, operations)
        if not all(pre_commit_results.values()):
            self._abort(tx_id)
            return False
        
        # Phase 3: DoCommit
        # 即使部分参与者超时,也可以继续
        self._do_commit_phase(tx_id, operations)
        return True
    
    def _can_commit_phase(self, tx_id, operations) -> Dict[str, bool]:
        """CanCommit 阶段"""
        results = {}
        for participant_id in operations:
            # 发送 CanCommit 请求,带超时
            result = self._send_with_timeout(
                participant_id, 
                'can_commit', 
                operations[participant_id],
                timeout=self.timeout
            )
            results[participant_id] = result
        return results
    
    def _pre_commit_phase(self, tx_id, operations) -> Dict[str, bool]:
        """PreCommit 阶段"""
        results = {}
        for participant_id in operations:
            result = self._send_with_timeout(
                participant_id,
                'pre_commit',
                tx_id,
                timeout=self.timeout
            )
            results[participant_id] = result
        return results
    
    def _do_commit_phase(self, tx_id, operations):
        """DoCommit 阶段"""
        for participant_id in operations:
            try:
                self._send_with_timeout(
                    participant_id,
                    'do_commit',
                    tx_id,
                    timeout=self.timeout
                )
            except TimeoutError:
                # 超时后继续,参与者会自行决策
                print(f"[3PC] {participant_id} 超时,继续执行")

3.4 3PC vs 2PC

特性 2PC 3PC
阻塞时间 长(直到收到决定) 短(PreCommit后超时继续)
网络往返 2 次 3 次
性能 较好 较差(多一次往返)
实现复杂度 简单 复杂
实际应用 广泛使用 较少使用

结论:3PC 理论上减少了阻塞,但增加了复杂度和延迟,实际应用较少。


四、Saga模式:长事务的解决方案

4.1 Saga的核心思想

Saga模式由 Hector Garcia-Molina 于 1987 年提出,用于解决**长事务(Long-Lived Transaction)**问题。

核心思想:

将大事务拆分为多个本地事务,每个本地事务有对应的补偿操作。

Saga 结构:

事务 1 ──> 事务 2 ──> 事务 3 ──> ... ──> 事务 N
   │          │          │               │
   ▼          ▼          ▼               ▼
补偿 1      补偿 2      补偿 3          补偿 N

执行顺序:
- 成功:T1 -> T2 -> T3 -> ... -> TN
- 失败:T1 -> T2 -> T3 (失败) -> C3 -> C2 -> C1

4.2 Saga的实现方式

编排式(Choreography):

python
class SagaOrchestrator:
    """
    Saga 编排器实现
    """
    
    def __init__(self):
        self.steps: List[SagaStep] = []
    
    def add_step(self, step: SagaStep):
        """添加 Saga 步骤"""
        self.steps.append(step)
    
    def execute(self, context: dict) -> bool:
        """
        执行 Saga
        
        成功:执行所有步骤
        失败:执行已执行步骤的补偿
        """
        executed_steps = []
        
        try:
            for step in self.steps:
                print(f"[Saga] 执行步骤: {step.name}")
                step.execute(context)
                executed_steps.append(step)
            
            print("[Saga] 所有步骤执行成功")
            return True
            
        except Exception as e:
            print(f"[Saga] 步骤失败: {e}")
            # 执行补偿
            self._compensate(executed_steps, context)
            return False
    
    def _compensate(self, executed_steps: List[SagaStep], context: dict):
        """执行补偿"""
        print("[Saga] 开始补偿")
        
        # 逆序执行补偿
        for step in reversed(executed_steps):
            try:
                print(f"[Saga] 补偿步骤: {step.name}")
                step.compensate(context)
            except Exception as e:
                # 补偿失败,需要人工介入或记录待处理
                print(f"[Saga] 补偿失败 {step.name}: {e}")
                self._log_compensation_failure(step, e)


class SagaStep:
    """Saga 步骤基类"""
    
    def __init__(self, name: str):
        self.name = name
    
    def execute(self, context: dict):
        """执行正向操作"""
        raise NotImplementedError
    
    def compensate(self, context: dict):
        """执行补偿操作"""
        raise NotImplementedError


# 电商订单 Saga 示例
class DeductInventoryStep(SagaStep):
    """扣减库存步骤"""
    
    def __init__(self):
        super().__init__("deduct_inventory")
    
    def execute(self, context: dict):
        product_id = context['product_id']
        quantity = context['quantity']
        
        # 调用库存服务
        result = inventory_service.deduct(product_id, quantity)
        context['inventory_deducted'] = result
        print(f"[Saga] 扣减库存: {product_id} x {quantity}")
    
    def compensate(self, context: dict):
        product_id = context['product_id']
        quantity = context['quantity']
        
        # 恢复库存
        inventory_service.restore(product_id, quantity)
        print(f"[Saga] 恢复库存: {product_id} x {quantity}")


class CreateOrderStep(SagaStep):
    """创建订单步骤"""
    
    def __init__(self):
        super().__init__("create_order")
    
    def execute(self, context: dict):
        user_id = context['user_id']
        product_id = context['product_id']
        
        order_id = order_service.create(user_id, product_id)
        context['order_id'] = order_id
        print(f"[Saga] 创建订单: {order_id}")
    
    def compensate(self, context: dict):
        order_id = context['order_id']
        
        order_service.cancel(order_id)
        print(f"[Saga] 取消订单: {order_id}")


class DeductBalanceStep(SagaStep):
    """扣减余额步骤"""
    
    def __init__(self):
        super().__init__("deduct_balance")
    
    def execute(self, context: dict):
        user_id = context['user_id']
        amount = context['amount']
        
        payment_service.deduct(user_id, amount)
        print(f"[Saga] 扣减余额: {user_id} - {amount}")
    
    def compensate(self, context: dict):
        user_id = context['user_id']
        amount = context['amount']
        
        payment_service.refund(user_id, amount)
        print(f"[Saga] 退款: {user_id} + {amount}")


# 使用示例
def demo_saga():
    saga = SagaOrchestrator()
    
    # 定义 Saga 流程
    saga.add_step(DeductInventoryStep())
    saga.add_step(CreateOrderStep())
    saga.add_step(DeductBalanceStep())
    
    # 执行上下文
    context = {
        'user_id': 'user-123',
        'product_id': 'product-456',
        'quantity': 1,
        'amount': 100.00
    }
    
    success = saga.execute(context)
    print(f"\nSaga 结果: {'成功' if success else '失败并补偿'}")

# demo_saga()

协同式(Orchestration)vs 编排式(Choreography):

特性 协同式 编排式
控制方式 中央协调器控制 事件驱动,各服务自主
复杂度 协调器复杂 服务间依赖复杂
可见性 好(集中管理) 差(分散在各服务)
适用场景 流程固定 流程动态

4.3 Saga的优缺点

优点:

  • 无全局锁,性能高
  • 支持长事务
  • 天然适合微服务架构

缺点:

  • 隔离性差(脏读)
  • 补偿逻辑复杂
  • 不保证即时一致性

隔离性问题的解决:

python
class SagaWithCompensatingTransaction:
    """
    语义锁(Semantic Lock)模式
    """
    
    def execute_with_semantic_lock(self, context):
        # 1. 设置语义锁(标记为"处理中")
        inventory_service.mark_pending(context['product_id'])
        
        try:
            # 2. 执行 Saga
            result = self.saga.execute(context)
            
            # 3. 成功:确认
            if result:
                inventory_service.confirm(context['product_id'])
            else:
                inventory_service.release(context['product_id'])
            
            return result
        except Exception:
            # 4. 异常:释放锁
            inventory_service.release(context['product_id'])
            raise

五、TCC模式:Try-Confirm-Cancel

5.1 TCC的核心思想

TCC(Try-Confirm-Cancel) 是 Saga 模式的一种特化,将每个操作拆分为三个阶段:

  • Try:预留资源,执行业务检查
  • Confirm:确认执行业务
  • Cancel:取消预留,释放资源
TCC 流程:

Try 阶段:
├── 检查业务条件
├── 预留资源(不真正扣减)
└── 记录预留状态

Confirm 阶段(成功):
├── 将预留转为实际
└── 释放预留状态

Cancel 阶段(失败):
├── 释放预留资源
└── 清理预留状态

5.2 TCC实现

python
class TCCService:
    """
    TCC 服务接口
    """
    
    def try_operation(self, context: dict) -> bool:
        """尝试预留资源"""
        raise NotImplementedError
    
    def confirm(self, context: dict):
        """确认执行"""
        raise NotImplementedError
    
    def cancel(self, context: dict):
        """取消预留"""
        raise NotImplementedError


class InventoryTCCService(TCCService):
    """
    库存服务的 TCC 实现
    """
    
    def try_operation(self, context: dict) -> bool:
        """
        Try:预留库存
        """
        product_id = context['product_id']
        quantity = context['quantity']
        
        # 1. 检查库存
        available = inventory_db.get_available(product_id)
        if available < quantity:
            return False
        
        # 2. 预留库存(冻结)
        inventory_db.freeze(product_id, quantity)
        
        # 3. 记录预留
        context['frozen_quantity'] = quantity
        print(f"[TCC-Try] 预留库存: {product_id} x {quantity}")
        return True
    
    def confirm(self, context: dict):
        """
        Confirm:实际扣减库存
        """
        product_id = context['product_id']
        quantity = context['frozen_quantity']
        
        # 1. 实际扣减
        inventory_db.deduct(product_id, quantity)
        
        # 2. 释放预留
        inventory_db.unfreeze(product_id, quantity)
        
        print(f"[TCC-Confirm] 确认扣减: {product_id} x {quantity}")
    
    def cancel(self, context: dict):
        """
        Cancel:释放预留
        """
        product_id = context['product_id']
        quantity = context.get('frozen_quantity', 0)
        
        if quantity > 0:
            # 释放预留
            inventory_db.unfreeze(product_id, quantity)
            print(f"[TCC-Cancel] 释放预留: {product_id} x {quantity}")


class TCCTransactionManager:
    """
    TCC 事务管理器
    """
    
    def __init__(self):
        self.participants: List[TCCService] = []
    
    def register(self, service: TCCService):
        """注册 TCC 服务"""
        self.participants.append(service)
    
    def execute(self, context: dict) -> bool:
        """
        执行 TCC 事务
        """
        confirmed = []
        
        try:
            # Phase 1: Try
            print("[TCC] Phase 1: Try")
            for service in self.participants:
                if not service.try_operation(context):
                    # Try 失败,开始 Cancel
                    print(f"[TCC] Try 失败,开始 Cancel")
                    self._cancel_all(confirmed, context)
                    return False
                confirmed.append(service)
            
            # Phase 2: Confirm
            print("[TCC] Phase 2: Confirm")
            for service in self.participants:
                service.confirm(context)
            
            print("[TCC] 事务成功")
            return True
            
        except Exception as e:
            # Confirm 失败,执行 Cancel
            print(f"[TCC] Confirm 异常: {e}")
            self._cancel_all(confirmed, context)
            return False
    
    def _cancel_all(self, services: List[TCCService], context: dict):
        """取消所有已 Try 的服务"""
        print("[TCC] 执行 Cancel")
        for service in reversed(services):
            try:
                service.cancel(context)
            except Exception as e:
                print(f"[TCC] Cancel 异常: {e}")


# 使用示例
def demo_tcc():
    manager = TCCTransactionManager()
    
    # 注册服务
    manager.register(InventoryTCCService())
    # manager.register(OrderTCCService())
    # manager.register(PaymentTCCService())
    
    # 执行事务
    context = {
        'product_id': 'product-123',
        'quantity': 2,
        'user_id': 'user-456',
        'amount': 200.00
    }
    
    success = manager.execute(context)
    print(f"\nTCC 结果: {'成功' if success else '失败'}")

# demo_tcc()

5.3 TCC vs Saga

特性 TCC Saga
资源预留 是(Try阶段) 否(直接执行)
隔离性 较好 较差
实现复杂度 高(三阶段) 中(两阶段)
性能 较低(多一次操作) 较高
适用场景 资源紧张、需要预留 一般业务流程

六、选型与实践

6.1 分布式事务模式对比

模式 一致性 性能 复杂度 适用场景
2PC 强一致性 低 中 传统单体拆分时
3PC 强一致性 较低 高 极少使用
Saga 最终一致性 高 中 微服务、长事务
TCC 最终一致性 中 高 资源预留场景
本地消息表 最终一致性 高 中 异步场景
MQ事务 最终一致性 高 中 消息驱动架构

6.2 选型决策树

需要强一致性?
├── 是 → 2PC / 3PC
│   └── 能容忍性能损失?
│       ├── 是 → 2PC
│       └── 否 → 优化 2PC(异步提交)
│
└── 否 → 需要资源预留?
    ├── 是 → TCC
    │   └── 能处理三阶段复杂度?
    │       ├── 是 → TCC
    │       └── 否 → Saga + 语义锁
    │
    └── 否 → Saga
        └── 流程固定?
            ├── 是 → 编排式 Saga
            └── 否 → 协同式 Saga

6.3 实际系统案例

Seata(阿里开源):

Seata 架构:

TC (Transaction Coordinator)
├── 维护全局事务状态
├── 驱动全局提交或回滚
└── 与 RM 通信

TM (Transaction Manager)
├── 定义全局事务范围
├── 开始全局事务
└── 提交或回滚全局事务

RM (Resource Manager)
├── 管理分支事务资源
├── 与 TC 通信注册分支
└── 驱动分支事务提交或回滚

模式支持:
├── AT 模式(自动补偿)
├── TCC 模式
├── Saga 模式
└── XA 模式(2PC)

使用示例:

java
@GlobalTransactional
public void purchase() {
    // 自动加入全局事务
    orderService.createOrder();
    storageService.deduct();
    accountService.debit();
    // 任一失败自动回滚
}

6.4 最佳实践

1. 避免分布式事务:

优先考虑:
├── 业务拆分,消除跨服务事务
├── 异步化,使用消息队列
└── 接受最终一致性

2. 幂等性设计:

python
def idempotent_operation(request_id: str, operation: callable):
    """
    幂等操作包装器
    """
    # 检查是否已处理
    if redis.exists(f"processed:{request_id}"):
        return redis.get(f"result:{request_id}")
    
    # 执行操作
    result = operation()
    
    # 记录结果
    redis.setex(f"processed:{request_id}", 3600, "1")
    redis.setex(f"result:{request_id}", 3600, result)
    
    return result

3. 监控与告警:

python
class TransactionMonitor:
    """
    事务监控
    """
    
    def on_transaction_timeout(self, tx_id: str):
        """事务超时告警"""
        alert_manager.send_alert(
            level="warning",
            message=f"事务 {tx_id} 超时",
            action="人工介入检查"
        )
    
    def on_compensation_failure(self, tx_id: str, step: str):
        """补偿失败告警"""
        alert_manager.send_alert(
            level="critical",
            message=f"事务 {tx_id} 补偿失败: {step}",
            action="立即人工处理"
        )

结语

分布式事务是微服务架构中最具挑战性的问题之一。从 2PC 的强一致性保证,到 Saga 的最终一致性妥协,再到 TCC 的资源预留模式,每种方案都在一致性、可用性和性能之间寻找平衡。

核心洞见:

  1. 没有银弹:不存在完美的分布式事务方案,只有最适合场景的方案
  2. 能不用就不用:通过业务设计避免分布式事务,是最优解
  3. 幂等性是基础:所有分布式操作都必须是幂等的
  4. 监控不可少:分布式事务的异常需要及时发现和处理

理解这些事务模式,不仅是掌握技术方案,更是理解分布式系统的本质:在不确定的网络环境中,如何做出可靠的承诺。


参考资源

经典论文:

  1. Gray, J. (1978). "Notes on Data Base Operating Systems". LNCS.
  2. Garcia-Molina, H., & Salem, K. (1987). "Sagas". ACM SIGMOD.
  3. Brewer, E. (2000). "Towards Robust Distributed Systems". PODC Keynote.
  4. Gilbert, S., & Lynch, N. (2002). "Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services". ACM SIGACT.

工程实践: 5. Seata 官方文档:https://seata.io/ 6. Saga 模式:https://microservices.io/patterns/data/saga.html 7. Microsoft CQRS Journey:https://msdn.microsoft.com/library/jj554200.aspx

进阶阅读: 8. Kleppmann, M. (2017). "Designing Data-Intensive Applications". O'Reilly. 9. Fowler, M. "Patterns of Enterprise Application Architecture". Addison-Wesley.


创建时间:2026年04月11日
更新时间:2026年04月11日