# TSO 分配 时间窗口

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

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

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