参考:《全球将于2027年停止插入闰秒 时间系统迎来重要调整》
三个时间概念
- UTC:国际协调世界时,负责定义标准民用时间。
- 系统墙上时钟(wall clock):应用通过系统调用读取的当前时间,可能被校时操作调整。
- 单调时钟(monotonic clock):用于测量时间间隔,正常情况下不会因常规墙上时钟校准而回拨。
即使取消闰秒,以下情况仍可能引发回拨:
| 原因 | 示例 |
|---|---|
| NTP/Chrony 校时 | 系统时间快了 500 ms,校时策略选择向后跳 500 ms |
| 人工修改时间 | 管理员执行 date -s 或 timedatectl set-time |
| 虚拟机或宿主机时钟异常 | 虚拟机恢复快照、迁移或时钟源异常 |
| 系统启动或时间同步初始化 | 本地时钟不准确,启动后被校正 |
| 闰秒处理(旧机制) | 某些操作系统或时间同步配置会重复一秒或调整时钟 |
NTP/Chrony 并不总是直接回拨系统时间。它们可以通过 step(直接跳变) 或 slew(逐渐调整时钟速率) 来修正偏差。具体行为取决于实现、偏差大小和配置。
因此,取消闰秒只能消除一种特殊的时间调整场景,无法解决系统时钟自身的不连续问题。
Snowflake 是如何处理时钟回拨的?
经典 Twitter Snowflake 的策略是:发现回拨就拒绝生成 ID,而不是假装时间没有倒退。
经典实现会在回拨时抛出异常,要求调用方处理;当同一毫秒内序列号耗尽时,则等待下一毫秒。Twitter 的开源实现及其后续移植普遍采用这一思路。
例如,某节点已经使用了时间戳 10000,系统时钟突然退回到 9990:
- 继续使用
9990:可能生成比之前更小的 ID,甚至在序列状态不完整时产生重复。 - 直接返回错误:牺牲一段时间的可用性,优先保障唯一性。
- 等待系统时钟重新追上
10000:如果时间源恢复正常,可以继续生成 ID。
需要强调的是,经典 Snowflake 的策略并不能保证全局 ID 严格递增。它主要保障单个生成器在机器 ID 不冲突、状态正确的前提下避免重复;不同节点之间的时钟偏差仍会影响排序。
Sonyflake 是如何处理时钟回拨的?
Sonyflake 的策略与经典 Snowflake 不同:它通过逻辑时间和序列号推进,允许生成器在墙上时钟尚未追上时继续生成 ID。
假设上次使用的逻辑时间是 1000,系统时间回拨后得到 990:
elapsedTime < current为假。- Sonyflake 不会将逻辑时间退回
990。 - 它继续递增序列号。
- 当序列号耗尽时,逻辑时间加 1,即使墙上时钟仍停留在过去。
这意味着,Sonyflake 不必因为短暂回拨立即停止生成 ID,但它会消耗未来的逻辑时间额度,并且在序列号耗尽时可能等待。
这也不是无条件的安全保证:如果回拨持续很久,逻辑时间可能领先真实时间;如果生成器重启、状态丢失,或者多个实例使用相同机器 ID,仍需要额外措施保证唯一性。
如果真的出现一个小时的时间调整,会怎样?
正常情况下,UTC 不再插入闰秒,无直接影响。
可是如果系统时钟突然回拨 1 小时,则可能造成可用性或排序问题:Snowflake 可能拒绝生成 ID,直到时钟追上;Sonyflake 可以继续推进逻辑时间,但逻辑时间会领先真实时间。
Sonyfalke 回拨期间持续生成 ID,逻辑时间最多可能领先墙上时钟 1 小时。实际领先程度取决于生成速率、序列号耗尽情况和时钟恢复过程。
当墙上时钟恢复正常后,生成器需要等待真实时间追上逻辑时间,才能恢复到正常的时间偏差范围。若生成速率较低、回拨期间生成的 ID 很少,实际影响可能小得多。