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)、同步距离/根延迟。偏移持续变大或同步源不可达时告警;同时监控 “业务侧时间异常的报错”(如耗时负数、版本回退)。

关联