bigywater.tech
MongoDB · 61

因果一致性会话

写完马上从 secondary 读,能不能读到自己刚写的数据?因果一致性会话是怎么做到的?这篇用 3000 轮写后读统计旧读比例,用 failpoint 让 secondary 落后,看读命令里多出来的 afterClusterTime。

2052 对 0
3000 次写后立刻读 secondary:不带会话与带因果会话读到旧值的次数
3,008 ms
secondary 落后时,因果会话的读等待 secondary 追上所花的时间(不带会话的读立刻返回旧值)
afterClusterTime
因果会话的读命令里额外携带的读关注参数

旧读的比例

实测 · 本机执行输出
  不带会话:2052 / 3000 次读到旧值;因果一致性会话(把写入的 operationTime 传给会话):0 / 3000 次

写 primary 后立刻读 secondary,3000 次里有 2052 次读到旧值;因果一致性会话把写入的 operationTime 传给读,0 次旧读。

确定性地落后

实测 · 本机执行输出
  secondary(已停止拉取)上不带会话的读:i = 5999(旧值)
  同一个 secondary 上,带因果一致性会话(operationTime=Timestamp(1791775788, 1))的读:等了 3008 ms 后返回 i = 'FRESH'(等到 failpoint 关闭、secondary 追上)

用 failpoint stopReplProducer 让 secondary 停止拉取 oplog:无会话的读立刻返回旧值,因果会话的读会阻塞到 secondary 追上才返回新值(这里等了约 3 秒)。

机制

实测 · 本机执行输出
  带因果会话的 find 命令里的 readConcern: {'afterClusterTime': Timestamp(1791775788, 1)}
  不带会话的 find 命令里的 readConcern: None

会话的读命令里多了 readConcern: {afterClusterTime: ...},服务端等到本节点应用到该时间点之后才执行读。

时间戳的来源

实测 · 本机执行输出
  写入后 s1.operation_time = Timestamp(1791775791, 1)
  新会话 s2 一开始 operation_time = None
  把 s1 的 operationTime 传给 s2 之后: Timestamp(1791775791, 1);读 → 'A'

每次响应都带 operationTime,会话把它记下;可以手工传给另一个会话(advance_operation_time)。

边界

实测 · 本机执行输出
  新会话没有传递 operationTime:读到 i = 'A'(旧值;因果一致性会话自己没有写过,也没有被告知要等谁)

只保证同一条因果链:没有传递时间戳的新会话没有任何保证。

和其它关注搭配

实测 · 本机执行输出
  # afterClusterTime 是 readConcern 的一个参数,可以和 local / majority / snapshot 任意搭配
  test> db.runCommand({find: "t", filter: {_id: 0}, readConcern: {level: "local", afterClusterTime: Timestamp(0, 1)}}).ok
  1

afterClusterTime 可以和各种读关注搭配;要避免被回滚撤销,还需要 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 副本集,启用 enableTestCommands 以使用 failpoint(stopReplProducer);旧读次数每次运行都不同(这次 2052)
    • 没有验证:单调读(从不同 secondary 读时是否会“回到过去”);因果一致性与 majority 读写关注的完整组合下的保证(读自己的写、单调写、写后写);分片集群里的 clusterTime 传播(gossip);其它驱动里 causalConsistency 的默认值;secondary 落后很久时 afterClusterTime 的超时(maxTimeMS)
    京ICP备2026067502号-1京公网安备11010502063487号