Spanner架构设计 #
一、整体架构 #
1.1 架构概览 #
text
┌─────────────────────────────────────────────────────────────┐
│ 客户端应用 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Spanner Frontend │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ SQL解析 │ │ 查询优化 │ │ 事务管理 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Spanserver │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Tablet │ │ Tablet │ │ Tablet │ │
│ │ (分片1) │ │ (分片2) │ │ (分片3) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ TrueTime API │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ GPS时钟 │ │ 原子钟 │ │ 时钟同步 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
1.2 核心组件 #
| 组件 | 功能 |
|---|---|
| Spanner Frontend | SQL解析、查询优化、事务协调 |
| Spanserver | 数据存储、事务执行、复制管理 |
| Tablet | 数据分片,管理一段主键范围 |
| TrueTime | 全局时间服务,提供时间戳 |
1.3 层次结构 #
text
Universe (全球部署)
├── Placement Driver (PD)
│ ├── 负载均衡
│ ├── 数据迁移
│ └── 故障恢复
│
├── Zone (区域)
│ ├── Spanserver集群
│ ├── 存储节点
│ └── TrueTime服务
│
└── Spanner实例
├── 数据库
├── 表
└── 索引
二、TrueTime机制 #
2.1 TrueTime原理 #
TrueTime是Spanner的核心创新,解决了分布式系统中的时钟同步问题。
text
TrueTime返回时间区间 TT = [earliest, latest]
特点:
├── 真实时间一定在区间内
├── 区间大小 = 2 × ε (ε为时钟误差)
├── 通常 ε = 1-10ms
└── 通过GPS和原子钟保证精度
2.2 时钟同步架构 #
text
┌─────────────────────────────────────────────────────────────┐
│ TrueTime Master │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ GPS接收器 │ │ 原子钟 │ │ 交叉校验 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Time Slave Daemon │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 时钟同步 │ │ 误差计算 │ │ 本地时钟 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
2.3 TrueTime API #
python
# TrueTime API概念
class TrueTime:
def now(self) -> Interval:
"""返回当前时间区间 [earliest, latest]"""
pass
def after(self, timestamp) -> bool:
"""判断时间戳是否确定已过去"""
return self.now().earliest > timestamp
def before(self, timestamp) -> bool:
"""判断时间戳是否确定还未到来"""
return self.now().latest < timestamp
2.4 外部一致性保证 #
text
事务提交流程:
1. 获取提交时间戳 s
2. 等待 TT.after(s) 为真
3. 返回提交结果
这保证了:
├── 事务T1提交后,T2才能开始
├── T2的时间戳一定大于T1
└── 所有观察者看到相同的事务顺序
三、数据分片 #
3.1 Tablet结构 #
text
Tablet = 数据分片
├── 管理一段主键范围 [start_key, end_key)
├── 包含多个副本(Replica)
├── 通过Paxos协议同步
└── 自动负载均衡
3.2 分片策略 #
text
按主键范围分片:
┌────────────┬────────────┬────────────┬────────────┐
│ Tablet 1 │ Tablet 2 │ Tablet 3 │ Tablet 4 │
│ [1-1000) │ [1000-2000)│ [2000-3000)│ [3000-4000)│
└────────────┴────────────┴────────────┴────────────┘
特点:
├── 自动分片(Split)
├── 自动合并(Merge)
├── 负载均衡
└── 支持热点分散
3.3 分裂与合并 #
text
分裂(Split)触发条件:
├── 数据量超过阈值 (约4GB)
├── 负载过高
└── 手动触发
合并(Merge)触发条件:
├── 数据量低于阈值
├── 负载过低
└── 自动触发
3.4 交错表分片 #
text
父表: Users
├── user_id (主键)
└── name
子表: Orders (交错在Users下)
├── user_id (主键第一部分)
├── order_id (主键第二部分)
└── amount
分片结果:
┌─────────────────────────────────────┐
│ Tablet 1: user_id [1-1000) │
│ ├── Users数据 │
│ └── 对应的Orders数据 │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Tablet 2: user_id [1000-2000) │
│ ├── Users数据 │
│ └── 对应的Orders数据 │
└─────────────────────────────────────┘
优势:
├── 父子数据物理存储在一起
├── 减少跨分片查询
└── 提高JOIN性能
四、数据复制 #
4.1 复制架构 #
text
每个Tablet有多个副本:
┌─────────────────────────────────────────────────────────────┐
│ Paxos Group │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Replica 1 │ │ Replica 2 │ │ Replica 3 │ │
│ │ (Leader) │ │ (Follower) │ │ (Follower) │ │
│ │ Region A │ │ Region A │ │ Region B │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
4.2 Paxos协议 #
text
写入流程:
1. 客户端发送写请求到Leader
2. Leader分配时间戳
3. Leader发送Prepare消息到Followers
4. 多数副本确认后,Leader发送Commit
5. 返回客户端写入成功
读取流程:
├── 强一致读: 从Leader读取
├── 过期读: 从任意副本读取
└── 只读事务: 使用时间戳从任意副本读取
4.3 副本类型 #
| 类型 | 说明 | 使用场景 |
|---|---|---|
| 读写副本 | 参与投票,可成为Leader | 默认类型 |
| 只读副本 | 不参与投票,只同步数据 | 读扩展 |
| 见证副本 | 参与投票,不存储数据 | 成本优化 |
4.4 多区域复制 #
text
区域配置示例:
┌─────────────────────────────────────────────────────────────┐
│ Multi-region Instance │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Region A │ │ Region B │ │ Region C │ │
│ │ (Primary) │ │ (Secondary) │ │ (Secondary) │ │
│ │ 读写副本 │ │ 读写副本 │ │ 只读副本 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
配置类型:
├── nam-eur-asia1: 北美、欧洲、亚洲
├── regional-us-central1: 单区域
└── 自定义配置
五、事务模型 #
5.1 事务类型 #
text
读写事务 (Read-Write Transaction)
├── 支持读写操作
├── 两阶段提交
├── 强一致性
└── 使用Paxos同步
只读事务 (Read-Only Transaction)
├── 只支持读取
├── 快照隔离
├── 可从任意副本读取
└── 时间戳读取
快照读 (Snapshot Read)
├── 单次读取
├── 指定时间戳或范围
├── 无锁
└── 最佳性能
5.2 两阶段提交 #
text
两阶段提交流程:
阶段1: Prepare
┌──────────┐ Prepare ┌──────────┐
│ Coordinator │ ──────────────▶ │ Participant │
└──────────┘ └──────────┘
│ │
│ Prepare OK │
│◀─────────────────────────────┘
阶段2: Commit
┌──────────┐ Commit ┌──────────┐
│ Coordinator │ ──────────────▶ │ Participant │
└──────────┘ └──────────┘
│ │
│ Commit OK │
│◀─────────────────────────────┘
5.3 事务隔离级别 #
| 隔离级别 | 说明 |
|---|---|
| 可串行化 | 最高隔离级别,完全隔离 |
| 外部一致性 | 跨事务的全局顺序保证 |
5.4 MVCC实现 #
text
多版本并发控制:
┌─────────────────────────────────────────────────────────────┐
│ 数据存储 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Key: user_1 │ │
│ │ ├── ts=100: {name: "Alice", age: 25} │ │
│ │ ├── ts=200: {name: "Alice", age: 26} │ │
│ │ └── ts=300: {name: "Alice Smith", age: 26} │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
读取时指定时间戳,获取对应版本的数据
六、查询处理 #
6.1 查询执行流程 #
text
SQL查询执行:
┌──────────┐
│ 客户端 │
└──────────┘
│ SQL语句
▼
┌──────────┐
│ SQL解析 │ → 生成语法树
└──────────┘
│
▼
┌──────────┐
│ 查询优化 │ → 生成执行计划
└──────────┘
│
▼
┌──────────┐
│ 分布式执行│ → 分发到多个节点
└──────────┘
│
▼
┌──────────┐
│ 结果聚合 │ → 返回客户端
└──────────┘
6.2 查询优化器 #
text
优化策略:
├── 基于代价的优化(CBO)
├── 基于规则的优化(RBO)
├── 索引选择
├── JOIN顺序优化
└── 分布式执行优化
6.3 执行计划 #
sql
EXPLAIN SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.user_id = o.user_id
WHERE u.user_id = 1;
text
查询计划示例:
┌─────────────────────────────────────────────────────────────┐
│ Distributed Union │
│ └── Distributed Cross Apply │
│ ├── Distributed Union (users) │
│ │ └── Table Scan (users, filter: user_id=1) │
│ └── Distributed Union (orders) │
│ └── Table Scan (orders, filter: user_id=1) │
└─────────────────────────────────────────────────────────────┘
七、存储引擎 #
7.1 存储结构 #
text
LSM-Tree存储:
┌─────────────────────────────────────────────────────────────┐
│ MemTable │
│ (内存中的有序表,写入缓冲) │
└─────────────────────────────────────────────────────────────┘
│ 刷盘
▼
┌─────────────────────────────────────────────────────────────┐
│ SSTable │
│ (磁盘上的有序文件,分层存储) │
│ Level 0: 最近刷盘的文件 │
│ Level 1: 合并后的文件 │
│ Level 2+: 更早的数据 │
└─────────────────────────────────────────────────────────────┘
7.2 写入流程 #
text
写入流程:
1. 写入WAL日志
2. 写入MemTable
3. MemTable满后刷盘为SSTable
4. 后台Compaction合并SSTable
7.3 读取流程 #
text
读取流程:
1. 查询MemTable
2. 查询Block Cache
3. 查询Level 0 SSTable
4. 查询其他Level SSTable
八、实例配置类型 #
8.1 区域配置 #
text
Regional Instance:
├── 数据存储在单个区域
├── 通常3个副本
├── 读写延迟低
└── 成本相对较低
适用场景:
├── 单区域应用
├── 对延迟敏感
└── 成本敏感
8.2 多区域配置 #
text
Multi-region Instance:
├── 数据跨区域复制
├── 默认配置多个读写区域
├── 全球低延迟读取
└── 更高可用性
配置示例:
├── nam-eur-asia1: 北美、欧洲、亚洲
├── nam3: 美国东部和美国中部
└── europe-west1: 欧洲西部
8.3 配置选择 #
| 因素 | 区域配置 | 多区域配置 |
|---|---|---|
| 用户分布 | 单一区域 | 全球分布 |
| 延迟要求 | 低延迟 | 全球低延迟 |
| 可用性要求 | 标准 | 极高 |
| 成本 | 较低 | 较高 |
九、总结 #
架构要点:
| 组件 | 核心功能 |
|---|---|
| TrueTime | 全局时间同步,外部一致性保证 |
| Tablet | 数据分片,自动负载均衡 |
| Paxos | 复制协议,高可用保证 |
| MVCC | 多版本控制,快照隔离 |
设计原则:
text
1. 全球一致性
└── TrueTime保证外部一致性
2. 水平扩展
└── 自动分片和负载均衡
3. 高可用
└── 多副本同步复制
4. 灵活部署
└── 区域/多区域配置
下一步,让我们学习Spanner的数据类型!
最后更新:2026-03-27