bigywater.tech
MongoDB · 67

复制延迟与 maxStalenessSeconds

从 secondary 读会读到多旧的数据?怎么限制?这篇用 failpoint 制造一个落后 95 秒的 secondary,对比不设和设 maxStalenessSeconds 的读,看驱动怎样把它排除在外。

19 对 1 次
50 次 secondary 读里读到旧值的次数:不设 maxStalenessSeconds 与设为 90
95 s
被停止拉取的 secondary 在 95 秒时的延迟(随时间线性增长)
90 秒
maxStalenessSeconds 的下限

怎么算延迟

实测 · 本机执行输出
  10 个样本中各 secondary 的延迟(秒)范围: 9902: 0~2, 9903: 0~2
  # hello 里的 lastWrite:客户端据此估计 staleness
  test> var h = db.hello(); ({lastWrite: h.lastWrite.lastWriteDate, majorityWrite: h.lastWrite.majorityWriteDate})
  {
    lastWrite: ISODate('2026-10-12T03:47:37.000Z'),
    majorityWrite: ISODate('2026-10-12T03:47:37.000Z')
  }

用 replSetGetStatus 里 primary 与 secondary 的 optimeDate 之差;秒级精度,健康时 0~2 秒。

落后会累积

实测 · 本机执行输出
  停止拉取后     5 s:延迟 9902(被停的) = 5 s;9903(正常的) = 1 s
  停止拉取后    35 s:延迟 9902(被停的) = 35 s;9903(正常的) = 1 s
  停止拉取后    65 s:延迟 9902(被停的) = 65 s;9903(正常的) = 1 s
  停止拉取后    95 s:延迟 9902(被停的) = 95 s;9903(正常的) = 1 s

停止拉取的 secondary,延迟等于停止的时长:95 秒后延迟 95 秒。

不加限制

实测 · 本机执行输出
  50 次 secondary 读:服务端分布 {'9902': 19, '9903': 31};读到明显落后(比 primary 少 5 次以上更新)的值 19 次

读偏好是 secondary 时,请求在两个 secondary 间分摊,落后的那台贡献了 19 次旧值读。

maxStalenessSeconds

实测 · 本机执行输出
  50 次 secondary + maxStalenessSeconds=90 的读:服务端分布 {'9902': 1, '9903': 49};明显落后的值 1 次(被停的 9902 延迟约 95 s,已超过 90 s)

设 90 后驱动排除 staleness 超过 90 秒的节点,50 次读只有 1 次漏到旧节点(估算靠心跳,有滞后)。

下限

实测 · 本机执行输出
  max_staleness=30:读失败 ConfigurationError: maxStalenessSeconds must be at least 90. maxStalenessSeconds is set to 30.
  max_staleness=90:读成功

最小 90 秒,也不能小于心跳间隔 + 10 秒;设 30 会报 ConfigurationError。

追赶

实测 · 本机执行输出
  failpoint 关闭后 3.0 s 延迟降到 0 s(积压的 oplog 一口气应用)

恢复拉取后积压的 oplog 批量应用,95 秒的积压 3 秒追平。

分步演示

复制延迟与 maxStalenessSeconds
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / MongoDB 9.0.2(Homebrew 的 mongod、mongos)/ mongosh 2.12.0 / pymongo 4.18.3;每个实验在临时目录里起全新的 mongod(独立 dbpath,端口 9900 以上,只监听 127.0.0.1,WiredTiger 缓存 0.25 GB),需要副本集或分片集群时在本机起多个进程;命令输出来自真实的 mongosh,批量写入、计时与多线程用 pymongo;计时用 time.perf_counter,数字每次运行会有小幅抖动,ObjectId、时间戳等每次运行都不同。3 个 mongod 副本集,enableTestCommands 用于 failpoint;落后通过 stopReplProducer 构造(不是真实的负载过高);旧值次数每次运行不同
    • 没有验证:真实的写负载过高、磁盘慢造成的延迟;flow control(流控)对延迟的限制;secondary 读加 w:majority 的写;maxStalenessSeconds 在分片集群(mongos)里的行为;staleness 的估算公式的精确细节
    京ICP备2026067502号-1京公网安备11010502063487号