物理时钟:时钟漂移、NTP 与不可信的时间
01-基础与模型
入门
约 15 分钟
#时间#NTP#时钟漂移#单调时钟
更新 2026-10-02
当前状态:未学
本文基于模型知识整理(生成时未联网核对),关键结论建议对照经典文献复核。
一句话定义
分布式系统中每台机器的物理时钟(石英晶体振荡器)都会漂移且可被外部校时服务突然调整,因此跨机器的墙钟时间既不精确也不单调,不能作为事件排序与超时判定的依据。
为什么重要
大量"看似正确"的系统设计暗含了"时间戳可跨机器比较"的假设,而这是分布式系统最经典的 bug 来源之一:备份覆盖新数据、过期判定失效、事件顺序颠倒。理解时钟的不可信性,才能理解为什么需要逻辑时钟(kp-004)与共识(kp-017)。
前置知识
kp-001;操作系统时钟的基本概念。
核心概念
- 墙钟(wall-clock time):
gettimeofday/Date.now()一类返回"日历时间"的时钟,受 NTP 校正影响,可能回拨或跳变。 - 单调时钟(monotonic clock):只增不减、用于测量时间间隔的时钟(如 Java
System.nanoTime()、Gotime.Since()),但其读数只在单机内有意义,跨机器不可比较。 - 时钟漂移(drift):石英钟每秒误差约百万分之几十,一天可漂移数十毫秒到数秒(与温度、机器年龄有关)。
- NTP:网络时间协议,向权威时钟源校准;校正方式为阶跃(突然跳变)或缓慢调整(slewing),两者都会破坏"时间平滑流逝"的假设。
- 闰秒:UTC 与地球自转对齐引入的整秒调整,曾多次造成真实系统大面积故障。
原理与机制
墙钟的两大不可信来源:
- 不精确:漂移 + NTP 校准误差,意味着两台机器对"同一时刻"的读数可以差开毫秒到秒级,且差距随时间波动。
- 不单调:NTP 阶跃校正或管理员改时间会让墙钟回拨;"先发生的事件却带更早时间戳"随时可能发生。
由此推出两条工程铁律:测时长用单调时钟;跨机器排序事件绝不能只用墙钟。跨机器唯一可靠的排序依据是消息传递关系(happens-before,见 kp-004),或者在协议内显式地把时间变成需要共识的对象(kp-017)。
让时间变可信的代价:Google Spanner 选择把时钟的不确定性显式建模——TrueTime API 返回区间 [earliest, latest],提交事务时等待到足够确保"提交时间戳已过"才返回(commit wait)。这是用延迟换时间正确性的著名工程折衷(见 kp-032)。
图示
机器A墙钟: ──────────╲______╱──────► NTP阶跃导致回拨
机器B墙钟: ─────╱──────╲───────────► NTP阶跃导致跳前
同一真实时刻 t0,A 显示 10:00:03.100,B 显示 10:00:03.250
→ 用两者时间戳比较事件先后不可靠
直观类比
每块手表都走得不太准,还经常被人手动拨快拨慢。你可以用一块表计时(单调钟),但不能拿两块表的时间直接判定"谁先谁后",除非两人先对表——而对表这个动作本身,就是分布式系统要解决的协调问题。
实例或案例
- 某系统用"备份节点时钟读数"决定覆盖关系,NTP 跳变后备份带着旧数据覆盖了主库,造成数据回退——典型的墙钟信任事故。
- 2012 年与 2015 年的闰秒导致多个大型站点(如部分 Linux 内核上的 Java 服务)hang 死,促成 Google 提出 smear(把 1 秒摊到 24 小时内平滑加)。
- Spanner(2012 论文)证明:给时钟配硬件与 GPS/原子钟授时,并对不确定性显式建模,墙钟也能支撑外部一致性——但前提是接受 commit wait 延迟与专用基础设施。
常见误区
- 误区一:"NTP 校准后时间就准了"。校准是周期性且带误差的,两次校准之间持续漂移,校正瞬间还会跳变。
- 误区二:"单调时钟可以跨机器比较"。单调钟读数只是本机起点任意的计数,跨机比较毫无意义。
- 误区三:"用数据库服务器时间给事件排序就安全了"。若多个应用节点各自取本地时间写库,依旧错序;必须由单一权威点(单库、TSO、TrueTime)发时间。
与其他知识点的关系
- kp-004:放弃物理时间、改用逻辑时序(happens-before)排序事件。
- kp-017:共识协议只用逻辑任期/日志位置,不依赖墙钟;超时只是活性手段。
- kp-032:Spanner 的 TrueTime 是"驯服"物理时间的完整案例。
自测题
- 为什么测量代码执行耗时必须用单调时钟?
答:墙钟可能因 NTP 校正回拨,导致测出负数或失真;单调时钟保证只增不减。
- "跨机器比较墙钟时间戳来排序事件"错在哪?
答:各机时钟漂移不同且会跳变,同一真实时刻读数不同,先发生的事件可能带更晚的时间戳。
- Spanner 如何在承认时钟不完美的前提下获得外部一致性?
答:TrueTime 返回时间区间并显式暴露不确定性,提交时 commit wait 至"确定已过提交时间戳",用等待延迟换取正确性。
延伸阅读
- Martin Kleppmann《DDIA》第 8 章"Unreliable Clocks"。
- James C. Corbett 等, "Spanner: Google's Globally-Distributed Database"(OSDI 2012)。
- David F. 体系下的 NTP/时钟同步综述:Mills, "Internet Time Synchronization: The Network Time Protocol"。