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