# TSO 分配: 时间窗口

__*TSO 时间窗口*__

![left](./student-guide/diagram/TSOTimeWindow_zh_001_20220509.png)
-------------------------------------------------------------
单点授时首先要解决的肯定是单点故障问题。我们说过,PD 集群通过 raft 可以保证在主节点宕机后马上选出新主,在短时间内恢复授时服务。那问题来了,如何保证新的 leader 产生的 TSO 一定大于旧的 leader 呢？

那就必须将旧 leader 的当前 TSO 存储起来,存储也必须是高可靠的,所以 TiDB 使用了 etcd 。但是,每产生一个时间戳都要保存吗？显然不行,那样时间戳的产生速度直接与磁盘 I/O 能力相关,会存在瓶颈的。如何解决性能问题呢？TiDB 采用预申请时间窗口的方式,这个过程如图所示。

当前 PD（leader）的 TSO 是 700 (为了便于讲解,虚拟值),PD 向 etcd 申请了一个"可分配的时间窗口"。要知道时间窗口的跨度是可以通过参数指定的,系统的默认配置是 3 秒,示例采用了默认配置,所以这个窗口的起点是 PD 当前 STO 700,时间窗口的终点就在 STO 703 处。写入 etcd 成功后,PD 将得到一个从 700 到 703 的"可分配时间窗口",在这个时间窗口内 PD 可以使用系统的物理时间作为高位,拼接自己在内存中累加的逻辑时间,对外分配时间戳。上述设计意味着,所有 PD 已分配时间戳的高位,也就是物理时间,永远小于 etcd 存储的最大值。那么,如果 PD leader 宕机,新主就可以读取 etcd 中存储的最大值,在此基础上申请新的"可分配时间窗口",这样新主分配的时间戳肯定会大于旧主了。此外,为了降低通讯开销,每个客户端一次可以申请多个时间戳,时间戳数量作为参数,由客户端传给 PD。但要注意的是,一旦在客户端缓存,多个客户端之间时钟就不再是严格单调递增的。