分布式数据一致性

深入理解分布式系统中的数据一致性问题:从 CAP 理论到 BASE 实践,掌握强一致性、最终一致性、因果一致性的实现方案,详解 2PC、3PC、TCC、Saga、Seata 等分布式事务模式。

分布式数据一致性

分布式系统中,数据一致性是架构设计的核心挑战。本文从 CAP/Base 理论出发,系统梳理一致性模型、分布式事务协议(2PC/3PC/TCC/Saga)以及实际工程中的折中方案。


1. CAP 与 BASE 理论

1.1 CAP 定理

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

        Consistency
            /\
           /  \
          /    \
         /------\      CP 系统:ZooKeeper、etcd、Consul、HBase
        /   CAP  \     AP 系统:Cassandra、DynamoDB、Eureka
       /----------\    CA 系统:单机数据库(非分布式)
  Partition Tolerance — Availability
特性说明
C(一致性)所有节点看到的数据一致(等同于所有节点访问的都是最新副本)
A(可用性)每个请求都能收到非错误响应(不保证数据最新)
P(分区容错)网络分区时系统仍能继续运行

注意:P 在分布式系统中是不可放弃的,所以实际上是在 C 和 A 之间做选择。网络分区是常态(节点故障、网络抖动),因此真正的决策是 CP vs AP

1.2 BASE 理论

BASE 是 **Basically Available(基本可用)、Soft state(软状态)、Eventually consistent(最终一致性)**的缩写,是对 CAP 中 AP 方案的延伸。

概念说明示例
基本可用允许响应时间变长、部分功能降级高峰期排队、返回缓存数据
软状态允许中间状态,数据不一致时间窗口订单"支付中" → “已支付”
最终一致不保证实时一致,但保证一段时间后一致主从复制延迟、DNS 传播

2. 一致性模型

2.1 一致性强度谱系

强一致性 ──────────────────────────────→ 弱一致性

线性一致性 > 顺序一致性 > 因果一致性 > 会话一致性 > 最终一致性 > 弱一致性

线性一致性:所有操作看起来像是按全局时钟顺序执行的(最强)
顺序一致性:所有操作按某种全局顺序执行,但不保证与物理时间一致
因果一致性:有因果关系的操作按顺序执行,无因果关系的可并发
最终一致性:不保证立即一致,但保证在没有新更新的情况下最终一致

2.2 典型系统的一致性选择

系统一致性模型原因
ZooKeeper线性一致性(CP)配置管理需要强一致
etcd线性一致性(CP)服务发现、领导选举
Cassandra可调(通常最终一致)高写入吞吐
MongoDB默认最终一致,可选强一致灵活配置
MySQL 主从最终一致(异步复制)性能优先
Redis Cluster最终一致异步复制

3. 分布式事务

3.1 两阶段提交(2PC)

协调者                    参与者(甲)              参与者(乙)
  │                        │                        │
  │──── Phase 1: Prepare ─→│                        │
  │───────────────────────→│──── Prepare ──────────→│
  │                        │                        │
  │←──── Yes/No ───────────│←──── Yes/No ───────────│
  │                        │                        │
  │  如果全部 Yes:          │                        │
  │──── Phase 2: Commit ──→│                        │
  │───────────────────────→│──── Commit ───────────→│
  │                        │                        │
  │  如果有 No:             │                        │
  │──── Phase 2: Abort ───→│                        │
  │───────────────────────→│──── Abort ────────────→│

2PC 的问题

问题说明
同步阻塞Prepare 阶段参与者锁定资源,等待协调者指令
单点故障协调者宕机,参与者长期锁定
数据不一致协调者宕机后恢复,部分参与者已提交,部分未提交
// 伪代码示意
interface TwoPhaseCommit {
    // Phase 1
    boolean prepare(Transaction tx);  // 参与者:锁定资源,写 redo/undo log
    
    // Phase 2
    void commit(Transaction tx);      // 参与者:真正提交,释放锁
    void rollback(Transaction tx);    // 参与者:回滚,释放锁
}

3.2 三阶段提交(3PC)

引入预提交阶段,降低协调者单点故障的影响。

CanCommit → PreCommit → DoCommit

改进:
- CanCommit:协调者询问参与者是否能执行(不锁定资源)
- PreCommit:参与者预执行,锁定资源
- DoCommit:真正提交

超时处理:
- 参与者在等待 PreCommit 时超时 → 直接中止
- 参与者在等待 DoCommit 时超时 → 认为已提交(单方决策)

仍存在的问题:网络分区时仍可能不一致

3.3 TCC(Try-Confirm-Cancel)

业务层面的分布式事务,将每个操作拆分为三个幂等方法。

public interface PaymentService {
    // Try:预留资源(如冻结账户余额)
    boolean tryPay(String accountId, BigDecimal amount);
    
    // Confirm:真正执行扣款
    boolean confirmPay(String accountId, BigDecimal amount);
    
    // Cancel:释放预留资源(解冻余额)
    boolean cancelPay(String accountId, BigDecimal amount);
}
执行流程:

TCC 协调器
  ├── Try 账号服务(冻结 100 元) ──→ 成功
  ├── Try 库存服务(预占库存) ──→ 成功
  ├── Try 优惠券服务(核销优惠券) ──→ 失败
  │
  └── 任一 Try 失败 → 执行所有 Cancel
      ├── Cancel 账号服务(解冻)
      └── Cancel 库存服务(释放库存)

若全部 Try 成功:
  └── 执行所有 Confirm
      ├── Confirm 账号服务(真正扣款)
      ├── Confirm 库存服务(真正出库)
      └── Confirm 优惠券服务(真正核销)

TCC 优缺点

  • 优点:无全局锁,性能高,业务层实现
  • 缺点:业务侵入性高,需写三份逻辑,幂等性要求高

3.4 Saga 模式

长事务拆分成本地事务序列,每个本地事务有对应的补偿操作

正向流程(事务链):
  T1: 创建订单 → T2: 扣减库存 → T3: 扣减余额 → T4: 发送通知

如果 T3 失败:
  执行补偿链(倒序):
    C3: 恢复余额 ← C2: 恢复库存 ← C1: 取消订单

            T1    T2    T3(fail)   T4
            ↓     ↓      ↓
           [✓]   [✓]    [✗]
                  ↑      ↑
                 C2 ←── C3(补偿)
            C1 ←──┘

两种 Saga 实现

类型协调方式代表框架适用场景
编排型(Choreography)事件驱动,各服务监听事件自行决策Axon、Eventuate简单流程
编排型(Orchestration)中央协调器统一调度Camunda、Netflix Conductor复杂流程

3.5 Seata 框架(阿里开源)

整合了 AT、TCC、Saga、XA 四种模式的一站式分布式事务解决方案。

Seata 三大角色:

TC(Transaction Coordinator):事务协调器,维护全局事务状态
TM(Transaction Manager):事务管理器,定义全局事务范围
RM(Resource Manager):资源管理器,管理分支事务资源

                    ┌────→ RM(订单服务)
TM(@GlobalTransactional)──→ TC(Seata Server)──┼────→ RM(库存服务)
                    └────→ RM(账户服务)

AT 模式(自动补偿)

  • Seata 代理数据源,自动记录前后镜像(Undo Log)
  • 全局提交:异步删除 Undo Log
  • 全局回滚:用 Undo Log 生成反向 SQL 回滚
-- 业务 SQL
UPDATE account SET balance = balance - 100 WHERE id = 1;

-- Seata 自动记录的 Undo Log
{
  "beforeImage": {"balance": 500},
  "afterImage": {"balance": 400},
  "sql": "UPDATE account SET balance = balance - 100 WHERE id = 1"
}

-- 回滚时生成的反向 SQL
UPDATE account SET balance = 500 WHERE id = 1;

4. 一致性方案的选择

场景推荐方案说明
强一致 + 短事务2PC / XA金融转账、库存扣减(绝对一致)
最终一致 + 长事务Saga + 补偿电商订单全流程
高并发 + 性能优先TCC秒杀、抢购
简单场景 + 框架支持Seata AT低侵入,自动生成补偿
跨异构系统消息队列 + 最大努力通知对账补单机制

5. 实际工程中的折中

电商下单场景的一致性强弱选择:

┌─────────────┬──────────────┬────────────────┐
│   操作       │ 一致性要求    │   实现方案      │
├─────────────┼──────────────┼────────────────┤
│ 库存扣减     │ 强一致        │ 数据库事务 + 乐观锁 │
│ 余额扣减     │ 强一致        │ TCC / 2PC      │
│ 订单创建     │ 强一致        │ 本地事务        │
│ 积分增加     │ 最终一致      │ 消息队列异步    │
│ 短信通知     │ 最终一致      │ 消息队列异步    │
│ 搜索索引更新 │ 最终一致      │ Canal + ES     │
│ 数据统计     │ 最终一致      │ 定时汇总        │
└─────────────┴──────────────┴────────────────┘

原则:能异步的就异步,能最终一致的不强求强一致

6. 总结

一致性强弱与性能的关系:

强一致(2PC/TCC/XA) ←──── 高延迟、低吞吐 ────→ 最终一致(异步消息)
     ↑                                          ↑
   数据安全                                  系统性能

工程中的黄金法则:
1. 核心业务(资金、库存)用强一致
2. 非核心业务用最终一致 + 补偿机制
3. 建立对账和补偿Job,作为最终兜底线
4. 监控一致性延迟指标,设定告警阈值

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页