💾 数据存储设计¶
⚠️ 时效性说明:本章涉及前沿模型/价格/榜单等信息,可能随版本快速变化;请以论文原文、官方发布页和 API 文档为准。
学习时间:6小时 | 难度:⭐⭐⭐ 中等 | 前置知识:数据库基础、02-核心组件详解
🎯 本章目标¶
- 掌握数据库分片策略(Range/Hash/一致性哈希)
- 理解分库分表的完整方案设计
- 掌握读写分离架构和主从复制模式
- 深入理解分布式事务的各种解决方案
- 了解NewSQL数据库的特性
📋 目录¶
1. 数据库分片¶
1.1 为什么需要分片¶
Text Only
单机数据库瓶颈:
- 存储瓶颈:单机磁盘有限(如2TB)
- 性能瓶颈:单机QPS有上限(如5000 QPS)
- 可用性:单点故障风险
分片解决方案:
将数据分散到多台数据库服务器上
分片前: 分片后:
┌──────────┐ ┌──────┐ ┌──────┐ ┌──────┐
│ 1亿条数据 │ │ 3300 │ │ 3300 │ │ 3400 │
│ 单机扛不住 │ → │ 万条 │ │ 万条 │ │ 万条 │
└──────────┘ │Shard1│ │Shard2│ │Shard3│
└──────┘ └──────┘ └──────┘
1.2 Range分片(范围分片)¶
Text Only
按范围划分数据:
user_id: 1~1000万 → Shard 1
user_id: 1000万~2000万 → Shard 2
user_id: 2000万~3000万 → Shard 3
┌─────────────────────────────────────────┐
│ user_id 范围 │
├──────────┬────────────┬─────────────────┤
│ 1~10M │ 10M~20M │ 20M~30M │
│ Shard 1 │ Shard 2 │ Shard 3 │
└──────────┴────────────┴─────────────────┘
| 优势 | 劣势 |
|---|---|
| 范围查询高效 | 数据分布不均(热点问题) |
| 实现简单 | 新用户都集中在最后一个分片 |
| 扩展方便(加新范围) | 需要监控各分片数据量 |
适用场景:时间序列数据(按月份分片)、自增ID且查询有范围条件
1.3 Hash分片(哈希分片)¶
Text Only
按哈希值划分数据:
shard = hash(user_id) % 分片数
hash(user_1) % 3 = 0 → Shard 0
hash(user_2) % 3 = 1 → Shard 1
hash(user_3) % 3 = 2 → Shard 2
hash(user_4) % 3 = 0 → Shard 0
┌──────────┐ hash(key)%3 ┌────────┐
│ 请求 │ ──────────────→ │ 0: S0 │
│ key=xxx │ │ 1: S1 │
└──────────┘ │ 2: S2 │
└────────┘
| 优势 | 劣势 |
|---|---|
| 数据分布均匀 | 范围查询需要查所有分片 |
| 避免热点 | 扩容时需要数据迁移 |
| 实现简单 | hash(key) % N, N变化影响大 |
适用场景:数据量大、主要按主键查询
1.4 一致性哈希分片¶
解决普通Hash分片在扩容时大量数据迁移的问题:
Text Only
普通Hash:hash % 3 → hash % 4
几乎所有数据都要重新分配!
一致性哈希:增加一个节点
只需要迁移 1/N 的数据
哈希环:
0
╱ ╲
S1 ╲
╱ S2
│ +S_new │ ← 新增节点
│ │
╲ ╱
S3 ╱
╲ ╱
(bottom)
只有S_new和下一个节点之间的数据需要迁移
1.5 分片策略对比¶
| 策略 | 数据均匀性 | 范围查询 | 扩容成本 | 实现复杂度 |
|---|---|---|---|---|
| Range | ❌ 不均匀 | ✅ 高效 | ✅ 简单 | 低 |
| Hash | ✅ 均匀 | ❌ 全扫描 | ❌ 大量迁移 | 低 |
| 一致性Hash | ✅ 较均匀 | ❌ 全扫描 | ✅ 少量迁移 | 中 |
| 复合分片 | ✅ 均匀 | ✅ 部分支持 | 中 | 高 |
2. 分库分表¶
2.1 垂直拆分 vs 水平拆分¶
Text Only
垂直拆分(按业务/字段拆分):
拆分前: 拆分后:
┌────────────────────┐ ┌──────────┐ ┌──────────┐
│ user_id │ │ user_id │ │ user_id │
│ username │ → │ username │ │ avatar │
│ email │ │ email │ │ bio │
│ avatar(大字段) │ │ (核心表) │ │ (扩展表) │
│ bio(大字段) │ └──────────┘ └──────────┘
└────────────────────┘
水平拆分(按行拆分):
拆分前: 拆分后:
┌──────────────────┐ ┌──────────┐ ┌──────────┐
│ 1亿行 orders │ → │ 5000万行 │ │ 5000万行 │
│ │ │ orders_0 │ │ orders_1 │
└──────────────────┘ └──────────┘ └──────────┘
2.2 垂直拆分详解¶
垂直分库(按业务拆分):
Text Only
单体数据库: 拆分后:
┌──────────────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ users表 │ │用户库 │ │订单库 │ │商品库 │
│ orders表 │ → │ users │ │ orders │ │products│
│ products表 │ │ addrs │ │ items │ │ stocks │
│ addresses表 │ └────────┘ └────────┘ └────────┘
│ order_items表 │
│ stocks表 │
└──────────────────┘
垂直分表(按字段拆分):
Text Only
适用:表中有大字段且访问频率不同
users表:
user_id, name, email, phone ← 高频访问
avatar, bio, preferences ← 低频访问
拆分为:
users_base (user_id, name, email, phone) ← 热数据
users_ext (user_id, avatar, bio, prefs) ← 冷数据
2.3 水平拆分详解¶
分片键选择是水平拆分最关键的决策:
| 分片键要求 | 说明 | 示例 |
|---|---|---|
| 高区分度 | 值的分布要均匀 | ✅ user_id, ❌ gender |
| 查询关联 | 大部分查询都会带上这个字段 | 订单表用user_id |
| 业务关联 | 同一业务的数据在同一分片 | 同一用户的订单在同一分片 |
| 不可变 | 分片键不应该修改 | ✅ user_id, ❌ status |
分片键选择示例:
Text Only
场景:电商订单表
方案1: 按 order_id 分片
✅ 按订单号查询快
❌ 查某用户所有订单需要全分片扫描
方案2: 按 user_id 分片
✅ 查某用户所有订单只需查一个分片
✅ 用户相关的订单都在一起
❌ 大卖家的订单可能集中
方案3: 按 user_id 分片 + order_id 包含user_id信息
✅ 两种查询都高效
order_id = user_id(高位) + 时间戳 + 序列号
2.4 分库分表中间件¶
| 中间件 | 类型 | 开发商 | 特点 |
|---|---|---|---|
| ShardingSphere | 客户端/代理 | Apache | 功能全面,生态好 |
| Vitess | 代理 | YouTube/CNCF | MySQL专用,K8s原生 |
| MyCat | 代理 | 社区 | 国内流行,已较少维护 |
| DBLE | 代理 | 爱可生 | MyCat的企业级分支 |
2.5 分库分表后的问题¶
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 跨分片查询 | 查询涉及多个分片 | 冗余数据/全局表/查询引擎 |
| 跨分片Join | 无法直接Join两个分片 | 应用层Join/宽表冗余 |
| 跨分片事务 | 分布式事务 | 柔性事务(Saga/TCC) |
| 全局排序 | ORDER BY + LIMIT | 各分片排序后归并 |
| 全局ID | 自增ID冲突 | Snowflake等分布式ID |
| 扩容 | 增加分片 | 一致性哈希/成倍扩容 |
Text Only
跨分片排序方案:
查询: SELECT * FROM orders ORDER BY created_at DESC LIMIT 10
Shard 0: TOP 10 orders (sorted) → [o1, o3, o5, ...]
Shard 1: TOP 10 orders (sorted) → [o2, o4, o6, ...]
Shard 2: TOP 10 orders (sorted) → [o7, o8, o9, ...]
│
归并排序
│
▼
取所有结果中TOP 10
3. 读写分离¶
3.1 读写分离架构¶
Text Only
┌──────────┐
│ 应用服务器 │
└──┬───┬───┘
│ │
写请求│ │读请求
│ │
┌────▼┐ ┌▼────┐
│Master│ │Proxy│ ← 读写分离中间件
│(主库)│ │ │
└──┬───┘ └──┬──┘
│ │
┌─────┼────────┼─────┐
│ │ │ │
┌────▼┐ ┌──▼───┐ ┌─▼────┐│
│Slave│ │Slave │ │Slave ││
│ 1 │ │ 2 │ │ 3 ││
└─────┘ └──────┘ └──────┘│
读请求分发到从库 │
└────────────────────┘
3.2 读写分离方案对比¶
| 方案 | 实现方式 | 优势 | 劣势 |
|---|---|---|---|
| 应用层 | 代码中判断读写路由 | 灵活、无额外组件 | 侵入业务代码 |
| 中间件 | ProxySQL/MaxScale | 透明、统一管理 | 增加一跳延迟 |
| 框架层 | Spring的AbstractRoutingDataSource | 半透明 | 框架绑定 |
3.3 主从延迟问题¶
Text Only
写请求时间线:
T1: 写入Master
T2: Master生成binlog
T3: Slave接收binlog
T4: Slave重放binlog(replay)
T5: Slave数据可读
主从延迟 = T5 - T1(通常 ms ~ s级)
问题场景:
用户注册 → 写Master → 立即查询 → 读Slave → 数据还没同步过来!
用户以为注册失败了!
解决方案:
| 方案 | 说明 | 适用场景 |
|---|---|---|
| 强制读主 | 关键读操作走主库 | 写后立即读的场景 |
| 延迟感知 | 判断从库延迟,超阈值走主库 | 一般场景 |
| 会话一致性 | 同一会话内写后读走主库 | 用户操作场景 |
| 半同步复制 | 至少一个从库确认才返回 | 数据一致性要求高 |
| 中间件感知 | ProxySQL自动判断延迟 | 通用方案 |
Python
class ReadWriteRouter:
"""读写路由器 - 解决主从延迟"""
def __init__(self, master, slaves):
self.master = master
self.slaves = slaves
self._force_master_keys = set() # 需要强制走主库的key
def write(self, key, value):
self.master.write(key, value)
# 标记这个key在短时间内需要走主库读
self._force_master_keys.add(key)
# 延迟清除(如2秒后清除)
self._schedule_remove(key, delay=2.0)
def read(self, key):
if key in self._force_master_keys:
return self.master.read(key) # 走主库
return self._get_slave().read(key) # 走从库
def _get_slave(self):
# 选择延迟最小的从库
return min(self.slaves, key=lambda s: s.replication_lag()) # lambda匿名函数:简洁的单行函数
4. 主从复制¶
4.1 复制模式对比¶
Text Only
异步复制(Async):
Master: WRITE → ACK(客户端) → 发送binlog给Slave
Slave: 接收 → 重放
✅ 写性能最高
❌ Master挂了可能丢数据
半同步复制(Semi-Sync):
Master: WRITE → 发送binlog → 至少1个Slave确认 → ACK(客户端)
Slave: 接收binlog → ACK(Master)
✅ 至少1个Slave有数据
❌ 写性能下降(等待Slave确认)
全同步复制(Sync):
Master: WRITE → 发送binlog → 所有Slave确认 → ACK(客户端)
Slave: 接收binlog → 重放 → ACK(Master)
✅ 数据绝对不丢
❌ 写性能最差,一个Slave慢就全部慢
4.2 复制模式对比表¶
| 特性 | 异步复制 | 半同步复制 | 全同步复制 |
|---|---|---|---|
| 写延迟 | 最低 | 中等 | 最高 |
| 数据安全 | 可能丢数据 | 至少1副本 | 不丢数据 |
| 可用性 | 最高 | 高 | Slave故障影响写 |
| 一致性 | 最终一致 | 较强 | 强一致 |
| 适用场景 | 日志、统计 | 通用业务 | 金融交易 |
| MySQL支持 | 默认模式 | 插件支持 | Group Replication |
4.3 复制拓扑¶
Text Only
主从复制: 链式复制:
┌──────┐ ┌──────┐
│Master│ │Master│
└──┬───┘ └──┬───┘
│ │
┌──┼──┐ ┌──▼───┐
│ │ │ │Slave1│
▼ ▼ ▼ └──┬───┘
S1 S2 S3 │
┌──▼───┐
│Slave2│
└──┬───┘
│
┌──▼───┐
│Slave3│
└──────┘
主主复制(双写): 树形复制:
┌──────┐ ⟷ ┌──────┐ ┌──────┐
│Master│ │Master│ │Master│
│ A │ │ B │ └──┬───┘
└──────┘ └──────┘ │
互相同步 ┌───┼───┐
S1 S2 S3
│ │
S4 S5
4.4 故障切换¶
Text Only
Master故障切换流程(自动):
1. 检测Master故障
├── 心跳超时
└── 连续多次检测失败
2. 选举新Master
├── 选择数据最新的Slave
└── 检查复制位点
3. 提升Slave为新Master
├── STOP SLAVE
├── RESET SLAVE ALL
└── 开启写入
4. 其他Slave指向新Master
└── CHANGE MASTER TO new_master
5. 应用更新连接
├── DNS切换
├── VIP(Virtual IP)漂移
└── 或Service Discovery更新
常用工具:
- MySQL: MHA(Master High Availability)
- MySQL: Orchestrator
- MySQL: ProxySQL
- PostgreSQL: Patroni
5. 数据一致性与分布式事务¶
5.1 分布式事务问题¶
Text Only
场景:电商下单
1. 订单服务:创建订单 → 订单DB
2. 库存服务:扣减库存 → 库存DB
3. 积分服务:增加积分 → 积分DB
如果步骤2失败,如何回滚步骤1?
如果步骤2成功但步骤3失败?
这就是分布式事务问题!
5.2 2PC(两阶段提交)¶
Text Only
┌──────────┐
│协调者(TC) │
└──┬───┬───┘
│ │
Phase 1: 准备 │ │
(Prepare) │ │
│ │
┌─────▼┐ ┌▼─────┐
│参与者A│ │参与者B│
│Prepare│ │Prepare│
│ OK? │ │ OK? │
└──┬────┘ └──┬───┘
│ YES │ YES
│ │
Phase 2: 提交 ▼ ▼
(Commit) ┌──────────┐
│ TC: 都OK │
│→ COMMIT │
└──┬───┬───┘
│ │
┌─────▼┐ ┌▼─────┐
│COMMIT│ │COMMIT│
│ A │ │ B │
└──────┘ └──────┘
| 优势 | 劣势 |
|---|---|
| 强一致性保证 | 同步阻塞,性能差 |
| 实现相对简单 | 协调者单点问题 |
| 数据库原生支持(XA) | 网络分区时可能阻塞 |
5.3 3PC(三阶段提交)¶
Text Only
Phase 1: CanCommit(询问)
TC → 参与者: "你能提交吗?"
参与者 → TC: "能/不能"
Phase 2: PreCommit(预提交)
TC → 参与者: "准备提交"
参与者: 进行预提交(但不真正提交)
参与者 → TC: "预提交完成"
Phase 3: DoCommit(正式提交)
TC → 参与者: "正式提交"
参与者: 提交事务
相比2PC的改进: - 增加了CanCommit阶段,减少资源锁定时间 - 参与者引入超时机制,避免无限等待 - 但仍然无法完全解决网络分区问题
5.4 Saga模式¶
Text Only
Saga = 一系列本地事务 + 补偿操作
正向操作:
T1(创建订单) → T2(扣减库存) → T3(扣款) → T4(通知)
如果T3失败:
C2(恢复库存) → C1(取消订单)
┌────┐ ┌────┐ ┌────┐ ┌────┐
│ T1 │ → │ T2 │ → │ T3 │ → │ T4 │ → 成功!
└────┘ └────┘ └──┬─┘ └────┘
│ 失败
▼
┌────┐ ┌────┐
│ C2 │ → │ C1 │ → 回滚完成
└────┘ └────┘
Saga的两种实现:
| 实现 | 说明 | 优势 | 劣势 |
|---|---|---|---|
| 编排式 (Choreography) | 各服务监听事件自行处理 | 简单、松耦合 | 难以追踪、循环依赖 |
| 协调式 (Orchestration) | 中心协调器控制流程 | 流程清晰、易管理 | 协调器复杂、单点风险 |
Python
# Saga协调器示例
class SagaOrchestrator:
def __init__(self):
self.steps = [] # [(forward_action, compensate_action)]
self.completed_steps = []
def add_step(self, forward, compensate):
self.steps.append((forward, compensate))
def execute(self, context):
for i, (forward, compensate) in enumerate(self.steps): # enumerate同时获取索引和值
try: # try/except捕获异常
result = forward(context)
self.completed_steps.append((compensate, context))
context.update(result)
except Exception as e:
print(f"Step {i} failed: {e}")
self._compensate()
raise
def _compensate(self):
"""按逆序执行补偿操作"""
for compensate, context in reversed(self.completed_steps):
try:
compensate(context)
except Exception as e:
print(f"Compensation failed: {e}")
# 记录日志,人工处理
5.5 TCC(Try-Confirm-Cancel)¶
Text Only
TCC三个阶段:
Try(尝试):
- 预留资源
- 冻结库存(不是真正扣减)
- 冻结余额(不是真正扣款)
Confirm(确认):
- 确认提交
- 冻结库存 → 真正扣减
- 冻结余额 → 真正扣款
Cancel(取消):
- 释放预留资源
- 解冻库存
- 解冻余额
示例 - 电商下单:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 订单服务 │ │ 库存服务 │ │ 支付服务 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
Try: │ 创建待确认订单│ 冻结库存 │ 冻结余额
│ │ │
▼ ▼ ▼
────── 全部Try成功?──────
│ │
是 否
│ │
Confirm: │ Cancel: │
确认订单 │ 取消订单 │
扣减库存 │ 解冻库存 │
扣减余额 │ 解冻余额 │
Java
// TCC接口定义
public interface TccService<T> { // interface定义类型契约
// Try: 预留资源
boolean tryAction(T context);
// Confirm: 确认提交
boolean confirm(T context);
// Cancel: 取消释放
boolean cancel(T context);
}
// 库存TCC实现
public class InventoryTccService implements TccService<OrderContext> { // extends继承;implements实现接口
@Override // @Override重写父类方法
public boolean tryAction(OrderContext ctx) {
// 冻结库存(available_stock -= quantity, frozen_stock += quantity)
return inventoryDao.freezeStock(ctx.getProductId(), ctx.getQuantity());
}
@Override
public boolean confirm(OrderContext ctx) {
// 扣减冻结库存(frozen_stock -= quantity)
return inventoryDao.deductFrozenStock(ctx.getProductId(), ctx.getQuantity());
}
@Override
public boolean cancel(OrderContext ctx) {
// 解冻库存(frozen_stock -= quantity, available_stock += quantity)
return inventoryDao.unfreezeStock(ctx.getProductId(), ctx.getQuantity());
}
}
5.6 分布式事务方案对比¶
| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 | 代表框架 |
|---|---|---|---|---|---|
| 2PC/XA | 强一致 | 低 | 低 | 数据库层面 | MySQL XA |
| 3PC | 强一致 | 低 | 中 | 理论为主 | 少有实现 |
| Saga | 最终一致 | 高 | 中 | 长事务、跨服务 | Seata Saga |
| TCC | 最终一致 | 中 | 高 | 短事务、金融 | Seata TCC |
| 本地消息表 | 最终一致 | 高 | 中 | 异步场景 | 自定义 |
| 事务消息 | 最终一致 | 高 | 中 | MQ场景 | RocketMQ |
5.7 本地消息表方案¶
Text Only
适用:不需要实时一致的场景
流程:
1. 业务操作 + 写消息表(在同一个本地事务中)
2. 定时任务扫描消息表,发送消息
3. 消费者处理消息
4. 处理成功后更新消息状态
┌──────────────┐
│ 订单服务 │
│ ┌──────────┐ │ ┌──────────┐
│ │ orders │ │ │ 定时任务 │──→ MQ ──→ 库存服务
│ │ messages │ │ ←─ │ 扫描未发送│
│ └──────────┘ │ │ 的消息 │
└──────────────┘ └──────────┘
关键SQL:
BEGIN;
INSERT INTO orders (...) VALUES (...);
INSERT INTO messages (topic, body, status) VALUES ('order_created', '{}', 'PENDING');
COMMIT;
6. NewSQL数据库¶
6.1 NewSQL的定位¶
Text Only
传统关系数据库 NoSQL NewSQL
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ ✅ ACID │ │ ✅ 扩展性 │ │ ✅ ACID │
│ ✅ SQL │ │ ✅ 高性能 │ │ ✅ SQL │
│ ❌ 扩展性 │ │ ❌ 无ACID │ │ ✅ 扩展性 │
│ ❌ 分布式 │ │ ❌ 弱SQL │ │ ✅ 分布式 │
└──────────┘ └──────────┘ └──────────────┘
= SQL + NoSQL的优势
6.2 TiDB¶
Text Only
TiDB架构:
┌──────────────────────────────────┐
│ TiDB Server │ ← SQL层(无状态,可水平扩展)
│ SQL解析 → 优化 → 执行 │
└──────────────┬───────────────────┘
│
┌──────────────▼───────────────────┐
│ PD │ ← 调度层(元数据 + 调度)
│ 全局时钟 + Region调度 │
└──────────────┬───────────────────┘
│
┌──────────────▼───────────────────┐
│ TiKV │ ← 存储层(分布式KV)
│ ┌────────┐ ┌────────┐ ┌────────┐│
│ │Region 1│ │Region 2│ │Region 3││ ← 每个Region是Raft Group
│ │(3副本) │ │(3副本) │ │(3副本) ││
│ └────────┘ └────────┘ └────────┘│
└──────────────────────────────────┘
TiDB特点:
| 特性 | 说明 |
|---|---|
| MySQL兼容 | MySQL协议兼容,迁移成本低 |
| 水平扩展 | 存储和计算分离,独立扩展 |
| 强一致 | Raft协议保证数据一致性 |
| HTAP | 同时支持OLTP和OLAP |
| 在线DDL | 不锁表的Schema变更 |
6.3 CockroachDB¶
| 特性 | 说明 |
|---|---|
| PostgreSQL兼容 | PG协议兼容 |
| 全球部署 | 内置跨地域复制 |
| Serializable | 默认最高隔离级别 |
| 自动分片 | 透明的Range-based分片 |
6.4 NewSQL vs 传统分库分表¶
| 维度 | 分库分表 | NewSQL(TiDB) |
|---|---|---|
| 运维复杂度 | 高(多个MySQL实例) | 中(统一管理) |
| SQL兼容 | 有限(跨分片查询受限) | 完全兼容 |
| 分布式事务 | 需要额外方案 | 内置支持 |
| 扩容 | 需要数据迁移 | 自动均衡 |
| 成熟度 | 非常成熟 | 相对较新 |
| 性能 | 单分片查询快 | 略低(分布式开销) |
| 适用规模 | 中大型 | 大型 |
7. 练习与延伸阅读¶
7.1 练习题¶
- 分片设计:为一个DAU 1亿的社交应用设计消息表的分片方案
-
考虑:用户间消息、群聊消息、消息搜索
-
读写分离:设计一个支持"写后立即读"的读写分离方案
-
分布式事务:为电商退款场景设计Saga方案
-
涉及:订单服务、库存服务、支付服务、积分服务
-
容量规划:
- 某电商平台日均500万订单
- 每条订单约2KB
-
设计分库分表方案(考虑数据增长5年)
-
方案选型:对比分析以下场景应该用分库分表还是NewSQL:
- 场景A:已有大量MySQL存储的电商系统
- 场景B:全新的金融交易系统
7.2 延伸阅读¶
- 《DDIA》第5章 — 复制
- 《DDIA》第6章 — 分区
- 《DDIA》第7章 — 事务
- TiDB官方文档 — 架构设计
- Seata官方文档 — 分布式事务方案
- 美团技术博客 — "大众点评订单系统分库分表实践"
- 阿里技术博客 — "分布式事务解决方案"
📝 本章小结¶
| 知识点 | 关键要点 |
|---|---|
| 分片策略 | Range适合范围查询、Hash分布均匀、一致性哈希扩容友好 |
| 分库分表 | 垂直按业务/字段拆分、水平按行拆分、分片键选择是核心 |
| 读写分离 | 缓解读压力、主从延迟是核心问题 |
| 主从复制 | 异步性能好但可能丢数据、半同步是折中 |
| 分布式事务 | 2PC强一致但性能差、Saga/TCC最终一致但灵活 |
| NewSQL | TiDB兼容MySQL + 分布式,是分库分表的替代方案 |
← 上一章:核心组件详解 | 下一章:分布式系统基础 →