分布式系统数据一致性有哪些知识点?

面试场景

面试官:”微服务架构下,你们是如何保证跨服务的数据一致性的?”

这是分布式系统的核心问题。回答需要体现:

  1. 理论基础(CAP/BASE)
  2. 方案选型(刚性/柔性事务)
  3. 具体实践

理论基础

CAP定理

分布式系统最多只能同时满足三项中的两项:

特性 含义 举例
Consistency 所有节点数据一致 A扣款100,B必须同时+100
Availability 每个请求都能收到响应 不能超时或报错
Partition Tolerance 网络分区时仍能工作 机房断网也能服务

现实选择:由于网络分区必然发生,P必须保证,所以只能在C和A之间选择:

  • CP:保证一致性,牺牲可用性(如ZooKeeper)
  • AP:保证可用性,牺牲强一致性(如Eureka)

BASE定理

对CAP中AP方案的延伸,用最终一致性替代强一致性

BASE = Basically Available + Soft State + Eventually Consistent
        基本可用           +   软状态   +     最终一致性
特性 说明 示例
基本可用 核心功能可用 大促时关闭非核心功能
软状态 允许中间状态 订单”处理中”状态
最终一致性 最终数据一致 5秒后库存和订单对上

事务方案分类

┌─────────────────────────────────────────────────────────┐
│                   分布式事务方案                          │
├────────────────────────┬────────────────────────────────┤
│      刚性事务          │           柔性事务              │
│     (强一致性)        │        (最终一致性)            │
├────────────────────────┼────────────────────────────────┤
│   • 2PC/XA            │   补偿型:                       │
│   • 3PC               │     • TCC                       │
│                       │     • SAGA                      │
│                       │   通知型:                       │
│                       │     • 本地消息表                 │
│                       │     • 事务消息                   │
└────────────────────────┴────────────────────────────────┘

刚性事务

2PC(两阶段提交)

阶段一:准备阶段

协调者 → 参与者1: 准备提交?
协调者 → 参与者2: 准备提交?

参与者1: 执行本地事务,不提交,返回OK
参与者2: 执行本地事务,不提交,返回OK

阶段二:提交阶段

协调者收到所有OK后 → 参与者1: 提交!
                  → 参与者2: 提交!

问题

  • 同步阻塞:资源锁定等待
  • 单点故障:协调者挂了,参与者一直阻塞
  • 数据不一致:部分提交成功部分失败

3PC(三阶段提交)

在2PC基础上增加CanCommit预询问阶段,并引入超时机制

改进点:

  • 参与者也有超时机制,超时自动提交
  • 预询问可以快速失败

问题:仍无法解决网络分区导致的不一致。


柔性事务

TCC(Try-Confirm-Cancel)

┌─────────────────────────────────────────────────────────┐
│  Try(尝试)                                             │
│    预留资源,检查业务可行性                               │
│    例:冻结库存100件                                     │
├─────────────────────────────────────────────────────────┤
│  Confirm(确认)                                         │
│    真正执行业务                                          │
│    例:扣减冻结的库存                                    │
├─────────────────────────────────────────────────────────┤
│  Cancel(取消)                                          │
│    释放预留资源                                          │
│    例:释放冻结的库存                                    │
└─────────────────────────────────────────────────────────┘

代码示例

// Try阶段
public boolean tryDeductStock(Long productId, int count) {
    // 冻结库存
    int affected = stockMapper.freezeStock(productId, count);
    return affected > 0;
}

// Confirm阶段
public boolean confirmDeductStock(Long productId, int count) {
    // 扣减冻结的库存
    stockMapper.confirmDeduct(productId, count);
    return true;
}

// Cancel阶段
public boolean cancelDeductStock(Long productId, int count) {
    // 释放冻结的库存
    stockMapper.unfreezeStock(productId, count);
    return true;
}

优点:性能好,无长时间锁 缺点:侵入性强,需要实现三个接口

SAGA

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

T1 → T2 → T3 → T4
         ↓ 失败
       C3 ← C2 ← C1(逆向补偿)

适用场景:业务流程长、参与方多

本地消息表

┌──────────────────────────────────────────────────────────┐
│                      订单服务                             │
│                                                          │
│   BEGIN TRANSACTION                                      │
│     1. INSERT INTO orders ...                           │
│     2. INSERT INTO local_message ...  (同一事务)        │
│   COMMIT                                                │
│                                                          │
│   定时任务扫描local_message,发送到MQ                     │
└──────────────────────────────────────────────────────────┘
             ↓
           MQ
             ↓
┌─────────────────────┐
│      库存服务        │
│  消费消息,扣减库存   │
└─────────────────────┘

优点:简单可靠 缺点:需要定时任务,延迟稍高


方案选型

方案 一致性 性能 侵入性 适用场景
2PC/XA 金融核心
TCC 最终 复杂业务
SAGA 最终 长流程
本地消息表 最终 一般业务
事务消息 最终 有RocketMQ

选型建议

金融级强一致性要求 → 2PC/XA或Seata AT
高性能要求 + 复杂业务 → TCC
长流程业务 → SAGA
一般业务 + 简单实现 → 本地消息表
已有RocketMQ → 事务消息

Seata AT模式

阿里开源的分布式事务框架,自动生成补偿SQL。

@GlobalTransactional  // 关键注解
public void createOrder(OrderRequest request) {
    // 1. 创建订单
    orderService.create(request);
    
    // 2. 扣减库存(远程调用)
    stockService.deduct(request.getProductId(), request.getCount());
    
    // 3. 扣减余额(远程调用)
    accountService.deduct(request.getUserId(), request.getAmount());
}

原理

  1. 业务SQL执行前记录undo_log
  2. 所有分支事务成功 → 删除undo_log
  3. 任一分支失败 → 根据undo_log回滚

面试答题框架

理论基础:
- CAP定理:分布式系统只能CP或AP
- BASE理论:最终一致性替代强一致性

方案分类:
- 刚性事务:2PC/3PC,强一致但性能差
- 柔性事务:TCC/SAGA/本地消息表,最终一致

选型原则:
- 金融强一致 → XA/Seata
- 一般业务 → 本地消息表
- 高性能 + 复杂逻辑 → TCC

实际项目用什么:
- Seata AT模式(非侵入式)
- 本地消息表(简单场景)

总结

知识点 核心内容
CAP 分布式系统的权衡取舍
BASE 最终一致性的理论基础
刚性事务 强一致性,性能差
柔性事务 最终一致性,性能好
选型原则 根据业务场景选择合适方案