bigywater.tech
MongoDB · 69

arbiter 与 w:majority 的陷阱

为什么副本集里放 arbiter 会让 w:majority 出问题?这篇搭一个 PSA,停掉唯一的 secondary,看 primary、写关注、majority 读和提交点的表现,并和 PSS 对比。

code 64
PSA 停掉 secondary 后 w:majority 的超时错误
5 到 9 秒
继续写入时 appliedOpTime 与 lastCommittedOpTime 的差距的增长
w:1
有 arbiter 时隐式默认写关注(不是 w:majority)

PSA

实测 · 本机执行输出
  成员: 9901=PRIMARY, 9902=SECONDARY, 9903=ARBITER
  # 有 arbiter 时的默认写关注
  test> var r = db.adminCommand({getDefaultRWConcern: 1}); ({defaultWriteConcern: r.defaultWriteConcern, defaultWriteConcernSource: r.defaultWriteConcernSource})
  {
    defaultWriteConcern: undefined,
    defaultWriteConcernSource: 'implicit'
  }
  arbiter 9903:hello.arbiterOnly = True;向它读数据:NotPrimaryError: node is not in primary or recovering state, full error: {'topologyVersion': {'pr(它没有数据)

primary + secondary + arbiter:arbiter 参与投票但不存数据,所以只有 2 个数据节点。

停掉 secondary

实测 · 本机执行输出
  w:1 0.2 ms;w:majority 5.0 ms

primary + arbiter 仍是多数,primary 不降级,但 w:majority 需要的是“多数节点持久化了这次写入”,arbiter 无法确认,于是超时。

隐式默认

实测 · 本机执行输出
  primary 还是 primary: isWritablePrimary = True
  w:1 的 insert:成功,0.6 ms
  不指定写关注(集群默认写关注是 implicit):insert 成功,0.3 ms——有 arbiter 时隐式默认是 w:1,不是 w:majority
  w:majority(wtimeout=2000):2012 ms 后 WTimeoutError(code 64)

有 arbiter 时隐式的默认写关注是 w:1,所以不显式指定写关注的写入看起来一切正常——它们并没有被多数确认。

majority 读

实测 · 本机执行输出
  刚写入的 {k: "w1"}:readConcern local → True;readConcern majority → False

提交点停住,readConcern: majority 读不到新数据;local 读得到。

提交点落后

实测 · 本机执行输出
  已多写 2000 个文档:appliedOpTime.ts = 1791777439.4;lastCommittedOpTime.ts = 1791777434.122(提交点相对开始时 没动);落后 5 秒
  已多写 4000 个文档:appliedOpTime.ts = 1791777440.4;lastCommittedOpTime.ts = 1791777434.122(提交点相对开始时 没动);落后 6 秒
  已多写 6000 个文档:appliedOpTime.ts = 1791777441.4;lastCommittedOpTime.ts = 1791777434.122(提交点相对开始时 没动);落后 7 秒
  已多写 8000 个文档:appliedOpTime.ts = 1791777442.4;lastCommittedOpTime.ts = 1791777434.122(提交点相对开始时 没动);落后 8 秒
  已多写 10000 个文档:appliedOpTime.ts = 1791777443.4;lastCommittedOpTime.ts = 1791777434.122(提交点相对开始时 没动);落后 9 秒

写入继续,但多数提交点不前进,差距越来越大,存储引擎要保留更多历史版本。

PSS 对比

实测 · 本机执行输出
  secondary 回来后:appliedOpTime.ts = 1791777443.4;lastCommittedOpTime.ts = 1791777443.4
  w:majority 又能成功了:5.8 ms
  PSS(primary + 2 个 secondary)停掉一个 secondary:w:majority 成功,28.1 ms

三个数据节点停掉一个,另外两个就能确认多数,w:majority 不受影响。

分步演示

arbiter 与 w:majority
本步的对象

    本文用到的工具与边界

    • 本机 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 副本集(第三个为 arbiterOnly),secondary 用 kill -9 停掉
    • 没有验证:提交点停住对 WiredTiger 缓存与 history store 的具体影响(只观察到差距在变大);长时间(小时级)后的磁盘与缓存压力;在 PSA 上把 secondary 降为 hidden 或延迟的变体;5.0 之后添加 arbiter 要求显式设置默认写关注的校验;MongoDB 对 arbiter 的官方建议
    京ICP备2026067502号-1京公网安备11010502063487号