Spanner TrueTime #

一、TrueTime概述 #

1.1 什么是TrueTime #

TrueTime是Spanner的核心创新,提供了全局统一的时间戳服务,是Spanner实现外部一致性的关键。

text
TrueTime特点:
├── 全局统一时间
├── 有界误差
├── 高精度(1-10ms)
├── 基于GPS和原子钟
└── 外部一致性保证

1.2 时间误差 #

text
TrueTime返回时间区间 [earliest, latest]
├── 真实时间一定在区间内
├── 区间大小 = 2 × ε
├── ε 通常为 1-10ms
└── 通过多时钟源保证精度

二、TrueTime架构 #

2.1 时钟源 #

text
TrueTime时钟源:
├── GPS接收器
│   ├── 接收卫星时间信号
│   ├── 提供绝对时间
│   └── 多个GPS源冗余
│
├── 原子钟
│   ├── 铯原子钟或铷原子钟
│   ├── 高精度本地时间
│   └── GPS故障时备用
│
└── 交叉校验
    ├── GPS与原子钟互相验证
    ├── 检测异常时钟
    └── 排除故障源

2.2 时间同步架构 #

text
┌─────────────────────────────────────────────────────────────┐
│                     TrueTime Master                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  GPS时钟    │  │  原子钟     │  │  交叉校验   │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                   Time Slave Daemon                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  时钟同步   │  │  误差计算   │  │  本地时钟   │         │
│  └─────────────┘  └─────────────┘  └─────────────────────┘         │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                      Spanserver                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ TrueTime API│  │  事务管理   │  │  时间戳分配 │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘

2.3 误差来源 #

text
时间误差来源:
├── 网络延迟: Master到Slave的通信延迟
├── 时钟漂移: 本地时钟的漂移率
├── 同步间隔: 两次同步之间的时间
└── 温度变化: 影响晶振频率

三、TrueTime API #

3.1 API接口 #

python
# TrueTime API概念
class TrueTime:
    def now(self) -> Interval:
        """
        返回当前时间区间 [earliest, latest]
        真实时间一定在此区间内
        """
        pass
    
    def after(self, timestamp) -> bool:
        """
        判断时间戳是否确定已过去
        即: earliest > timestamp
        """
        return self.now().earliest > timestamp
    
    def before(self, timestamp) -> bool:
        """
        判断时间戳是否确定还未到来
        即: latest < timestamp
        """
        return self.now().latest < timestamp

3.2 时间区间 #

text
TrueTime.now() 返回 [earliest, latest]

示例:
├── 当前时间: 2024-03-27 10:00:00.000
├── 误差 ε: 5ms
├── earliest: 2024-03-27 09:59:59.995
├── latest: 2024-03-27 10:00:00.005
└── 真实时间一定在 [earliest, latest] 内

3.3 提交等待 #

python
# 事务提交流程
def commit_transaction():
    # 1. 获取提交时间戳
    s = max(commit_ts, TrueTime.now().latest)
    
    # 2. 等待时间戳确定已过去
    while not TrueTime.after(s):
        wait()
    
    # 3. 返回提交结果
    return s

四、外部一致性 #

4.1 外部一致性定义 #

text
外部一致性保证:
├── 事务T1在事务T2之前提交
├── T1的提交时间戳 < T2的提交时间戳
├── 所有观察者看到相同的事务顺序
└── 跨区域全局一致

4.2 实现原理 #

text
外部一致性实现:
1. 事务T1获取提交时间戳 s1
2. 等待 TrueTime.after(s1) 为真
3. 返回T1提交结果
4. 事务T2开始,获取时间戳 s2 > s1

这保证了:
├── T1的提交时间戳确定已过去
├── T2的时间戳一定大于T1
└── 所有观察者看到相同顺序

4.3 与其他一致性对比 #

一致性级别 说明
线性一致性 单操作原子性
顺序一致性 单节点顺序
因果一致性 有因果关系的顺序
外部一致性 全局实时顺序

五、时间戳类型 #

5.1 提交时间戳 #

sql
-- 创建表时启用提交时间戳
CREATE TABLE logs (
    log_id INT64 NOT NULL,
    message STRING(MAX),
    commit_ts TIMESTAMP OPTIONS (allow_commit_timestamp = true)
) PRIMARY KEY (log_id);

-- 插入时使用提交时间戳
INSERT INTO logs (log_id, message, commit_ts)
VALUES (1, 'Test message', PENDING_COMMIT_TIMESTAMP());

5.2 读取时间戳 #

java
// Java读取时间戳
import com.google.cloud.spanner.TimestampBound;

// 强读(最新时间戳)
client.singleUse().executeQuery(...);

// 指定时间戳读取
Timestamp timestamp = Timestamp.ofTimeMicroseconds(1234567890);
client.singleUse(TimestampBound.ofReadTimestamp(timestamp))
    .executeQuery(...);

5.3 时间戳边界 #

java
// 最小过期读
TimestampBound.ofMinReadTimestamp(timestamp);

// 最大过期读
TimestampBound.ofMaxStaleness(10, TimeUnit.SECONDS);

// 精确过期读
TimestampBound.ofExactStaleness(10, TimeUnit.SECONDS);

六、应用场景 #

6.1 金融交易 #

java
// 金融交易需要严格的时间顺序
public void processTransaction(DatabaseClient client, 
                               String fromAccount, 
                               String toAccount, 
                               double amount) {
    client.readWriteTransaction().run(transaction -> {
        // 转账操作
        // TrueTime保证事务顺序
        // 确保资金一致性
        
        return null;
    });
}

6.2 审计日志 #

sql
-- 使用提交时间戳记录操作时间
CREATE TABLE audit_logs (
    log_id INT64 NOT NULL,
    user_id INT64,
    action STRING(100),
    commit_ts TIMESTAMP OPTIONS (allow_commit_timestamp = true)
) PRIMARY KEY (log_id);

-- 插入审计日志
INSERT INTO audit_logs (log_id, user_id, action, commit_ts)
VALUES (1, 100, 'LOGIN', PENDING_COMMIT_TIMESTAMP());

6.3 时间点查询 #

sql
-- 查询特定时间点的数据
SELECT * FROM accounts
FOR SYSTEM_TIME AS OF TIMESTAMP '2024-03-27T10:00:00Z';

-- 查询时间范围内的数据
SELECT * FROM accounts
FOR SYSTEM_TIME BETWEEN 
    TIMESTAMP '2024-03-27T09:00:00Z' AND 
    TIMESTAMP '2024-03-27T10:00:00Z';

七、TrueTime监控 #

7.1 关键指标 #

text
TrueTime监控指标:
├── clock_skew: 时钟偏差
├── time_error: 时间误差
├── commit_wait_time: 提交等待时间
└── timestamp_bound: 时间戳边界

7.2 查看时间误差 #

sql
-- 通过系统表查看
SELECT * FROM INFORMATION_SCHEMA.INSTANCES;

7.3 监控建议 #

text
监控建议:
├── 监控时钟偏差
├── 监控提交等待时间
├── 设置告警阈值
└── 定期检查时钟同步状态

八、TrueTime限制 #

8.1 误差影响 #

text
误差影响:
├── 提交等待时间: 等待时间戳确定已过去
├── 事务延迟: 增加事务提交延迟
├── 跨区域延迟: 更大的时间误差
└── 需要合理规划部署

8.2 最佳实践 #

text
TrueTime最佳实践:
├── 减少跨区域事务
├── 批量操作减少事务次数
├── 使用只读事务
├── 合理设置时间戳边界
└── 监控时钟同步状态

九、总结 #

TrueTime核心要点:

要点 说明
时间区间 [earliest, latest]
误差 通常1-10ms
外部一致性 全局实时顺序
提交等待 确保时间戳已过去

最佳实践:

text
1. 理解时间误差
   └── 真实时间在区间内

2. 使用提交时间戳
   └── 记录精确操作时间

3. 监控时钟状态
   └── 确保时钟同步

4. 优化事务设计
   └── 减少提交等待影响

5. 利用时间点查询
   └── 历史数据访问

下一步,让我们学习索引!

最后更新:2026-03-27