23.2 NTP 与 TrueTime
一句话
时钟误差 ≈ 漂移 × 同步周期:周期越长越省事,但误差越大(本机算例:8s→0.8ms、64s→6.4ms、300s→30ms);TrueTime 不去 “消除误差”,而是把误差显式成区间
[earliest, latest],提交时等过这个区间。
误差从哪来
| 来源 | 量级 | 说明 |
|---|---|---|
| 晶振漂移 | 10–100 ppm | 每天 0.86–8.6 秒 |
| 同步周期 | 与周期成正比 | 两次同步之间误差累积 |
| 网络不对称 | 0.1–10ms | NTP 的往返时延无法对半分 |
| 闰秒/人工改时 | 跳变 | 可能回拨(见 23.1 物理时钟与漂移) |
实测:误差与提交等待
漂移 100ppm,不同同步周期下的误差与 TrueTime 等待(2ε):
| 同步周期 | 最大误差 ε | 提交需等待 2ε |
|---|---|---|
| 8s | ±0.80ms | 1.60ms |
| 64s | ±6.40ms | 12.80ms |
| 300s | ±30.0ms | 60.0ms |
这解释了 Spanner 为什么要 “原子钟 + GPS”:把 ε 压到 1–7ms 量级,提交只需等几毫秒;普通 NTP 若按 300s 同步,等 60ms 才能提交——跨地域写延迟直接翻倍。
可用性 vs 延迟:周期短 → 误差小、提交快、但 NTP 流量大
周期长 → 省流量、误差大、提交慢
代码:NTP 与 TrueTime 的形态
# chrony:把同步周期调短以压小误差(代价是更多 NTP 流量)
server ntp1.example.com iburst minpoll 4 maxpoll 6 # 16s–64s
makestep 1.0 3 # 前 3 次允许跳变
// TrueTime 风格:API 返回区间,而不是点值
type Interval struct{ Earliest, Latest time.Time }
func (tt *TrueTime) Now() Interval { return Interval{...} } // [now-ε, now+ε]
// 提交规则:等到 Latest 过去,才能断言“我的提交早于下一个事务”
func commit(tt *TrueTime) {
now := tt.Now()
time.Sleep(time.Until(now.Latest)) // 等过不确定区间
}
反模式
| 反模式 | 后果 |
|---|---|
| 只同步一次就长期运行 | 漂移累积,跨机时间戳不可比 |
| 同步周期很长还要求毫秒级排序 | 误差大于业务精度,排序错乱 |
| 用 NTP 时间做 “分布式锁租约” 且不留余量 | 时钟回拨导致双主(见 18.6 脑裂与防护) |
| 忽略网络不对称 | 误差被低估,TrueTime 区间算小 |
| 把 TrueTime 当 “更准的 NTP” | 它的价值是 “给出误差界”,不是 “更准” |
| 不监控时钟偏移 | 偏移悄悄变大,直到出事故 |
三条铁律
🔑 误差 = 漂移 × 周期;要精度就缩短周期或换硬件。
🔑 TrueTime 的价值是 “区间”,不是 “点值”。
🔑 租约/超时要留时钟余量(通常 ≥10%)。
自检
Q1 为什么 NTP 无法做到 “完全准确”?
▸ 答案:因为 NTP 通过 “往返时延的一半” 估计偏移,而网络往返不对称(上行/下行延迟不同)无法消除;加上晶振漂移,两次同步之间误差会累积。所以 NTP 只能给出 “有界误差”,不能给出 “精确时间”。
Q2 TrueTime 的 [earliest, latest] 区间有什么用?
▸ 答案:它让系统能显式推理不确定性:如果两个事务的时间区间不重叠,就能确定先后;重叠就等待或重试。这比 “假装时间是准的” 安全得多——Spanner 的外部一致性就建立在这个区间上。
Q3 为什么提交要等 2ε 而不是 ε?
▸ 答案:因为要覆盖两个方向的误差:本机时间可能是 true+ε,对端可能是 true-ε,两者相差最多 2ε。等到 latest 过去,才能保证 “我的提交时刻一定早于下一个事务的开始”。
Q4 NTP 同步周期越短越好吗?
▸ 答案:不是:周期短 → 误差小,但 NTP 流量与服务器压力上升。工程上按 “业务需要的时间精度” 定:毫秒级排序需要短周期(如 16–64s);只做日志时间戳可以更长(几分钟)。
Q5 时钟回拨会带来什么事故?
▸ 答案:三类:耗时算出负数(用墙钟测耗时)、租约提前失效导致双主、基于时间的去重/排序错乱(后写的被当更早)。防护是 “用单调时钟测耗时、逻辑时钟定序、租约留余量”。
Q6 没有原子钟/GPS,能实现类似 TrueTime 吗?
▸ 答案:可以用 “保守的误差界”:按最坏漂移 × 同步周期估算 ε,并把等待时间加进提交路径。代价是提交变慢(ε 大),但语义仍正确。Spanner 的优势是 “用硬件把 ε 压小”,不是 “只有它能做”。
Q7 怎么监控 “时钟是否可靠”?
▸ 答案:三个指标:偏移量(chronyc tracking 的 offset)、漂移率(frequency)、同步距离/根延迟。偏移持续变大或同步源不可达时告警;同时监控 “业务侧时间异常的报错”(如耗时负数、版本回退)。