MongoDB 4.0 提供了level == “snapshot” 的readConcern。 该level 的readConcern 本质上和Primary Secondary 无关, 主要解决的问题是:
时间点1: session 1 打开一个cursor 用于读数据
时间点2: session 2 修改了 session 1 要读的数据,并且commit 了
时间点3: session 1 读到了 session 2 修改的数据。
最终造成了 session 1 读取到的数据 并不是 “时间点1” 的数据。
snapshot 正是用于解决上述问题。
使用方法
需要启动一个transaction 并指定readConcern level == “snapshot”
session.startTransaction({readConcern: {level: "snapshot"}})
var cursor = coll.find({..})
while (cursor.hasNext()) {
printjson(cursor.next());
}
session.commitTransaction()
实现原理
一个正常的读请求的逻辑如下:
while (!batch.full() && wtCursor.hasNext()) {
next = wtCursor.next();
if (filter.matches(next) {
batch.add(next);
}
if (timeToYield()) {
checkForInterrupt();
wtCursor.saveState();
releaseLocksAndWTSnapshot();
reacquireLocksAndWTSnapshot();
wtCursor.restoreState();
}
}
对于一般的的cursor,即使在同一个batch 内部,MongoDB 也会在yield 的时候 release/reacquireLocksAndWTSnapshot. 导致读的不同阶段也会读取到不同时间点的snapshot 数据。主要用于减少因为snapshot 对内存产生的额外压力。
对于snapshot cursor, MongoDB 不会做上述的2个步骤。对内存多了一些压力,但提供了snapshot read 的结果