连载中 7/16

回拨治理三招:等待、拒绝与扩展位

2026-06-08 · 5867 阅读 · 0 评论 · 0 赞

药方开三副,按幅度抓药

第 6 篇的病理报告结论:回拨幅度决定处理等级。所以治理方案从来不是三选一,而是按幅度分级的组合拳:小回拨硬扛、中回拨绕行、大回拨止损。逐副拆。

第一招:自旋等待,硬扛毫秒级

回拨几十毫秒以内,最朴素的应对是等它自己追回来:lastTimestamp 停在回拨前的位置,自旋等到系统时钟重新走过那个点,再正常发号。等待期间发出的号全部安全,因为时间戳始终不小于历史最大值:

if (now 前于 lastTimestamp) {
  差值 = lastTimestamp - now;
  if (差值 小于等于 阈值) {
    自旋等待 差值 毫秒后继续;   // 典型阈值 100ms
  } else {
    升级到第二招;
  }
}

两个细节决定这招安全不安全:一,必须设等待上限——回拨一秒还傻等一秒,吞吐归零;超限立刻升级。二,等待用 sleep 或短自旋,别把 CPU 烧成暖宝宝;等待计数打点上报,回拨频率是时钟健康度的直接指标。

第二招:拒绝发号,把风险挡在门外

秒级以上的回拨,等待失去意义——回拨多久不可知,等下去是黑洞。正确姿势是快速失败:抛出明确异常,拒绝发号。听着粗暴,其实把矛盾转给了上游容灾:

客户端拿到「发号失败」,重试路由到集群里其他时钟正常的节点——回拨通常只影响个别机器,集群里总有健康节点。业务侧的注册中心健康检查配合剔除,带病节点自动下线。这一招的前提是发号服务多节点部署,单点雪花节点无路可退(所以发号器必须集群化,第 14 篇展开)。

第三招:备用机器号,给时间线开岔

但有一种场景第二招无解:NTP 集中校时导致全集群同时回拨——健康节点一个不剩,重试也是死。这时用第三招:换机器号。

原理想通极妙:重复号的构成是「旧时间戳加同一机器号」。既然时间戳已经回去了,那就换一个没在历史里出现过的机器号——三维里有一维变了,ID 就不会和历史重合。给每台节点多分配几个备用 workerID,回拨超阈值时切到下一个备用号,时间线等于开了条岔路:

节点 7 主号发到时间 T 后回拨 2 秒
检测到 大幅回拨 -> 切备用机器号 107
备用号从回拨后的时间继续发
时间 T x 机器 7 的组合永远不再出现,唯一性保住

代价要心里有数:机器号要多留余量(每节点备二三个备用号),监控上要把「切换过备用号」的节点打上带病标记,运维定位这台机器的时钟为什么回拨。Leaf 的实现选择了更保守的路线——检测到回拨直接抛异常等待人工,把第三招留给业务自己实现,两条路线按团队运维能力选。

组合成一张流程图

nextId() 检测到 now 前于 lastTimestamp
   |
   v  判定回拨幅度
   |-- 几十 ms 内 --------> 自旋等待追平(招一)
   |-- 几百 ms 到秒级 ----> 拒绝发号,路由健康节点(招二)
   |-- 秒级以上 ----------> 切备用机器号继续发(招三)
   |                       同时告警 + 节点标记 + 人工跟进

预防优于治疗

代码治标,运维治本。发号节点的时钟纪律三条:一,NTP 用 slew 模式——让时钟以每秒零点几毫秒的速率缓慢逼近,避免 step 跳变;二,校时分批——绝不搞全集群统一校时,一批几台,错峰进行;三,时钟监控——节点间两两比对偏移量,偏差超阈值提前告警,别等撞号了才知道时钟病了。

小结

回拨治理一句话:毫秒级等待、秒级绕行、大步开岔,NTP 分批限速治本。三招组合下来,时钟回拨从死穴降级为可控风险。但整套方案都建立在一个前提上:每台节点的机器号是对的、不重复的。这个「身份证」怎么发、怎么续,就是下一篇的 workerID 之争。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#分布式ID#时钟回拨#自旋等待#备用机器号#Leaf

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞