MongoDB事务虽不直接依赖系统时钟逻辑判断,但其分布式协调(如2PC提交、oplog回放、分片均衡、锁管理)高度依赖节点间时间一致;clock_skew超限(默认300ms)会导致事务卡住、回滚失败或数据不一致,根本原因是底层时间基础失准。

MongoDB事务本身不直接依赖系统时钟做逻辑判断,但整个集群的分布式协调机制(尤其是事务提交、oplog回放、分片均衡、锁管理)高度依赖各节点时间的一致性。一旦出现 clock_skew,事务可能卡住、回滚失败、甚至引发数据不一致——这不是事务代码写得不对,而是底层时间基础塌了。
为什么事务提交会因 clock_skew 失败
MongoDB 4.0+ 的多文档事务使用两阶段提交(2PC),协调者(coordinator)和参与者(participant)之间通过心跳、超时、oplog时间戳协同。其中关键点:
-
logicalSessionId和txnNumber需配合lsid时间戳字段做幂等校验,而该时间戳来自gettimeofday();若某节点快了 500ms,它生成的事务开始时间可能“早于”其他节点已提交的 oplog 条目,导致OplogOutOfOrder - 分片集群中,
mongos向shard分发事务请求时,会附带readTimestamp(基于当前节点时间构造)。若目标 shard 时间慢了 300ms,它会拒绝该读时间点,报错ReadConcernMajorityNotAvailableYet - 事务超时由
maxTimeMS控制,但实际计时器依赖本地clock_gettime(CLOCK_MONOTONIC);而跨节点的“事务可见性窗口”却靠CLOCK_REALTIME对齐——两者失配时,一个节点认为事务已过期,另一个还认为有效
分片集群报 clock skew of the cluster 错误的直接原因
错误日志里出现 caught exception while doing balance: error checking clock skew of cluster ... :: caused by :: 13650 clock skew,说明 balancer 在尝试获取分布式锁前做了显式时间比对,且偏差超限(默认阈值是 300ms):
- 这个检查发生在
mongos启动时、以及每次 balance 周期开始前,调用的是内部checkClockSkew函数 - 它会向所有 config server 和 shards 发起
isMaster请求,并提取响应头里的localTime字段(即服务器当前new Date()值),再与自身时间比对 - 只要任意两个节点间差值 >300ms,就直接中断 balance,并拒绝后续分布式锁申请(
distributed lock acquisition failed) - 注意:这个检查不依赖 NTP 守护进程是否运行,只看当前时刻的
localTime差值——所以即使 chronyd 正在微调,只要瞬时 offset 超限,就会触发
chronyd / ntpd 配置必须满足的三个硬性条件
光装 chronyd 不等于时间同步达标。生产环境必须同时满足:
- 启动前执行
chronyc tracking,确认Offset稳定在 ±50ms 内;若首次启动偏差大,需配置makestep 1.0 -1(允许开机时一步修正 1 秒内偏移) - 禁用任何
date -s、hwclock --set或容器内timedatectl set-time类手动干预;虚拟机迁移后必须触发chronyc makestep手动矫正 - 所有 mongod/mongos/config server 必须共享同一组内网 NTP 源(如
192.168.10.1),严禁混用公网 pool.ntp.org 和局域网服务器——不同源的 stratum 层级和网络抖动会导致隐性 drift
最容易被忽略的容器与云环境陷阱
容器化或云主机部署下,时钟问题更隐蔽:
- Docker 默认不共享宿主机时钟;必须加
--volume /etc/localtime:/etc/localtime:ro,且确保宿主机本身已用 chronyd 同步到位 - Kubernetes Pod 中,若启用了
hostPID: true或hostNetwork: true,但没同步宿主机时间,Pod 内chronyd会因无法访问 NTP 端口而 fallback 到系统时钟漂移 - AWS EC2 实例启用
Amazon Time Sync Service后,仍需关闭ntpd并改用chronyd,否则两者冲突导致ntpq -p显示多个 source 但 offset 持续波动 - 阿里云/华为云等平台的“NTP 服务”只是辅助,真正要监控的是每个实例上
chronyc sources -v输出的^*标记源是否稳定,以及chronyc tracking | grep "Last offset"是否持续


















