目录
进入MongoDB中文手册(4.2版本)目录
借助MongoDB的 因果一致性客户端会话( causally consistent client sessions),读写策略的不同组合可提供不同的 因果一致性保证(causal consistency guarantees)。如果定义因果一致性表示持久化,则下表列出了各种组合提供的特定保证:
读策略 | 写策略 | 读自己的写入 | 单调读 | 单调写 | 写跟读 |
---|---|---|---|---|---|
“majority” | “majority” | ✅ | ✅ | ✅ | ✅ |
“majority” | { w: 1 } | ✅ | ✅ | ||
“local” | { w: 1 } | ||||
“local” | “majority” | ✅ |
如果因果一致性表示持久性,那么从表中可以看出,只有具有"majority"读策略的读操作和具有"majority"写策略的写操作才能保证所有四个因果一致性保证。也就是说, 因果一致的客户端会话只能保证以下方面的因果一致性:
- 有"majority"读策略的读操作;也就是说,读操作将返回大多数副本集成员已确认且持久化的数据。
- 有"majority"策略的写操作也就是说,写操作要求确认该操作已应用于大多数副本集的有投票权的成员。
如果因果一致性不表示着持久化(即,写操作可能会回滚),则具有{ w: 1 }写策略的写操作也可以提供因果一致性。
注意
在某些情况下(但不一定在所有情况下),读和写策略的其他组合也可以满足所有四个因果一致性保证。
读策略"majority"和写策略 "majority"确保即使在副本集中的两个成员暂时认为它们是主节点的情况下(例如,使用网络分区),这四个因果一致性保证也成立 。尽管两个主节点都可以完成具有{ w: 1 }写策略的写操作,但是只有一个主节点可以完成具有"majority"写策略的写操作。
例如,考虑网络分区划分五个成员副本集的情况:
基于以上分区:
- 有"majority"写策略的写操作可以在Pnew的上完成,但在Pold的上无法完成。
- 具有{ w: 1 }写策略的写操作可以在Pold或Pnew上完成。但是,一旦这些成员重新获得与副本集其余成员的通信,对Pold写入操作(以及复制到S1的写入操作)将回滚。
- 有"majority"写策略的写入操作在成功完成对Pnew的写入之后,具有 "majority"读策略的因果一致的读操作可以观察到对Pnew,S2和S3的写入 。该读操作在这些成员与副本集的其余成员通信并与副本集的其他成员进行同步后,就可以观察到Pold和S1上的写入。 在分区期间写入Pold的和(或)复制到S1的所有写入内容都会回滚。
1 场景
为了说明读写策略的要求,在以下场景中,一个客户端向副本集发出了一系列读写策略的各种组合的操作:
- 读策略"majority"和写策略"majority"
- 读策略"majority"和写策略{w: 1}
- 读策略"local"和写策略"majority"
- 读策略"local"和写策略{w: 1}
1.1 读策略"majority"和写策略"majority"
在因果一致的会话中使用读策略"majority"和写策略"majority"可提供以下因果一致性保证:
✅ 读自己的写入 ✅ 单调读 ✅ 单调写 ✅ 写跟读
(1)场景1(读策略"majority"和写策略"majority")
在具有两个主节点的短暂期间内,由于只有Pnew才能满足{ w: “majority” }写策略的写操作,因此客户端会话可以成功发出以下操作序列:
序列 | 示例 |
---|---|
1. 有写策略"majority"的写操作Write1写入Pnew | 对于item A,更新qty为50。 |
2. 有读策略"majority"的读操作Read1从S2读取 | 读取item A |
3. 有写策略"majority"的写操作Write2写入Pnew | 对于qty小于或等于50的item,更新restock为true |
4. 有读策略"majority"的读操作Read2从S3读取 | 读取item A |
因果一致性保证 | 描述 |
---|---|
✅ 读自己的写入 | Read1从S2读取的数据,反映了Write1之后的状态。 Read2从S1读取的数据,反映了Write1之后的状态,且Write2在Write1之后执行。 |
✅ 单调读 | Read2从S3读取的数据,反映Read1之后的状态。 |
✅ 单调写 | Write2在Pnew更新的数据,反映了Write1之后的状态。 |
✅ 写跟读 | Write2在Pnew更新的数据,以反映Read1之后的状态(即,较早的状态反映Read1读取的数据)。 |
(2)场景2(读策略"majority"和写策略"majority")
考虑另外一个序列,其中具有读策略"majority"的Read1路由到S1:
序列 | 示例 |
---|---|
1. 有写策略"majority"的写操作Write1写入Pnew | 对于item A,更新qty为50。 |
2. 有读策略"majority"的读操作Read1从S1读取 | 读取item A |
3. 有写策略"majority"的写操作Write2写入Pnew | 对于qty小于或等于50的item,更新restock为true |
4. 有读策略"majority"的读操作Read2从S3读取 | 读取item A。 |
在这个序列中,直到多数提交点在Pold上进行,Read1才返回。这是在Pold和S1可以与副本集的其余成员可以通信时才会发生。这个时候,Pold已退出(如果尚未退出),并且这两个成员从副本集的其他成员同步(包括Write1)。
因果一致性保证 | 描述 |
---|---|
✅ 读自己的写入 | Read1反映了Write1之后的状态。尽管在网络分区已修复并且该成员已与副本集的其他成员进行同步之后。 Read2从S3读取的数据,反映了Write1之后的状态,且Write2在Write1之后执行。 |
✅ 单调读 | Read2从S3读取的数据,反映Read1之后的状态(即,较早的状态反映Read1读取的数据)。)。 |
✅ 单调写 | Write2在Pnew更新的数据,反映了Write1之后的状态。 |
✅ 写跟读 | Write2在Pnew更新的数据,反映了Read1之后的状态(即,较早的状态反映Read1读取的数据)。 |
1.2 读策略"majority"和写策略{w: 1}
如果因果一致性表示持久化,则在因果一致性会话中使用读策略"majority"和写策略{ w: 1 }可提供以下因果一致性保证:
❌ 读自己的写入 ✅ 单调读 ❌ 单调写 ✅ 写跟读
如果因果一致性并不表示持久性:
✅ 读自己的写入 ✅ 单调读 ✅ 单调写 ✅ 写跟读
(1)场景3(读策略"majority"和写策略{w: 1})
在具有两个主节点的短暂期间内,因为Pold和Pnew都能满足有写策略{ w: 1 }的写操作,一个客户端会话可以成功地发出以下的操作序列,但不是因果关系一致,如果因果一致表示持久化:
序列 | 示例 |
---|---|
1. 有写策略"{ w: 1 }"的写操作Write1写入Pold | 对于item A,更新qty为50。 |
2. 有读策略"majority"的读操作Read1从S2读取 | 读取item A。 |
3. 有写策略"{ w: 1 }"的写操作Write2写入Pnew | 对于qty小于或等于50的item,更新restock为true。 |
4. 有读策略"majority"的读操作Read2从S3读取 | 读取item A。 |
在这个序列中:
- Read1不能返回,直到多数提交点已经在Pnew上超过Write1的时间。
- Read2不能返回,直到多数提交点已经在Pnew上超过Write2的时间。
- 网络分区修复后,Write1将回滚。
如果因果一致性表示持久化
因果一致性保证 | 描述 |
---|---|
❌读自己的写入 | Read1从S2读取的数据不会反映Write1之后的状态。 |
✅ 单调读 | Read2从S3读取的数据,反映Read1之后的状态(即,较早的状态反映Read1读取的数据)。)。 |
❌ 单调写 | Write2在Pnew更新的数据,不会反映Write1之后的状态。 |
✅ 写跟读 | Write2在Pnew更新的数据,反映了Read1之后的状态(即,较早的状态反映Read1读取的数据)。 |
如果因果一致性不表示持久化
因果一致性保证 | 描述 |
---|---|
✅ 读自己的写入 | Read1从S2读取的数据,反映了和Write1相同的状态,且Write2在Write1之后执行。 |
✅ 单调读 | Read2从S3读取的数据,反映Read1之后的状态(即,较早的状态反映Read1读取的数据)。)。 |
✅ 单调写 | Write2在Pnew更新的数据,反映了和Write1相同的状态,且Write1执行之后会回滚。 |
✅ 写跟读 | Write2在Pnew更新的数据,反映了Read1之后的状态(即,其较早的状态反映Read1读取的数据)。 |
(2)场景4(读策略"majority"和写策略{w: 1})
考虑另外一个序列,其中具有读策略"majority"的Read1路由到S1:
序列 | 示例 |
---|---|
1. 有写策略"{w: 1}"的写操作Write1写入Pold | 对于item A,更新qty为50。 |
2. 有读策略"majority"的读操作Read1从S1读取 | 读取item A |
3. 有写策略"{w: 1}"的写操作Write2写入Pnew | 对于qty小于或等于50的item,更新restock为true |
4. 有读策略"majority"的读操作Read2从S3读取 | 读取item A。 |
在这个序列中:
- 直到多数提交点在S1上进行,Read1才返回。这是在Pold和S1可以与副本集的其余成员可以通信时才会发生。这个时候,Pold已退出(如果尚未退出),Write1从Pold和S1回滚,并且这两个成员从副本集的其他成员同步。
如果因果一致性表示持久化
因果一致性保证 | 描述 |
---|---|
❌读自己的写入 | Read1从S2读取的数据不会反映Write1的结果,且Write1已经回滚的。 |
✅ 单调读 | Read2从S3读取的数据,反映Read1之后的状态(即,其较早的状态反映Read1读取的数据)。)。 |
❌ 单调写 | Write2在Pnew更新的数据,不会反映Write1之后的状态,Write1早于Write2执行且已经回滚。 |
✅ 写跟读 | Write2在Pnew更新的数据,反映了Read1之后的状态(即,其较早的状态反映Read1读取的数据)。 |
如果因果一致性不表示持久化
因果一致性保证 | 描述 |
---|---|
✅ 读自己的写入 | Read1读取的数据,反映了Write1回滚之后的最终结果。 |
✅ 单调读 | Read2从S3读取的数据,反映Read1之后的状态(即,较早的状态反映Read1读取的数据)。)。 |
✅ 单调写 | Write2在Pnew更新的数据,和Write1之后相同,且Write1执行之后会回滚。 |
✅ 写跟读 | Write2在Pnew更新的数据,反映了Read1之后的状态(即,其较早的状态反映Read1读取的数据)。 |
1.3 读策略"local"和写策略{w: 1}
在因果一致的会话中使用读策略"local"和写策略不能{ w: 1 }保证因果一致性。
❌ 读自己的写入 ❌单调读 ❌ 单调写 ❌写跟读
在某些情况下(但不一定在所有情况下),此组合可以满足所有四个因果一致性保证。
(1)场景5(读策略"local"和写策略{w: 1})
在这个短暂期间内,因为Pold和Pnew都能满足有写策略{ w: 1 }的写操作,一个客户端会话可以成功地发出以下的操作序列,但不是因果关系一致的:
序列 | 示例 |
---|---|
1. 有写策略"{ w: 1 }"的写操作Write1写入Pold | 对于item A,更新qty为50。 |
2. 有读策略"local"的读操作Read1从S1读取 | 读取item A。 |
3. 有写策略"{ w: 1 }"的写操作Write2写入Pnew | 对于qty小于或等于50的item,更新restock为true。 |
4. 有读策略"local"的读操作Read2从S3读取 | 读取item A。 |
因果一致性保证 | 描述 |
---|---|
❌读自己的写入 | Read2从S3读取的数据,只反映Write2之后不会反映Write1的结果,且Write2在Write1之后执行。 |
❌单调读 | Read2从S3读取的数据,不反映Read1之后的状态(即,其较早的状态不会反映Read1读取的数据)。)。 |
❌ 单调写 | Write2在Pnew更新的数据,不会反映Write1之后的状态。 |
❌ 写跟读 | Write2在Pnew更新的数据,不反映Read1之后的状态(即,其较早的状态不反映Read1读取的数据)。 |
1.4 读策略"local"和写策略"majority"
在因果一致的会话中使用读策略"local"和写策略 “majority"可提供以下因果一致性保证:
❌ 读自己的写入 ❌单调读 ✅ 单调写 ❌写跟读
在某些情况下(但不一定在所有情况下),此组合可以满足所有四个因果一致性保证。
(1)场景5(读策略"local"和写策略"majority”)
在这个短暂期间内,因为只有Pnew能满足有写策略"majority"的写操作,一个客户端会话可以成功地发出以下的操作序列,但不是因果关系一致的:
序列 | 示例 |
---|---|
1. 有写策略"majority"的写操作Write1写入Pnew | 对于item A,更新qty为50。 |
2. 有读策略"local"的读操作Read1从S1读取 | 读取item A。 |
3. 有写策略"majority"的写操作Write2写入Pnew | 对于qty小于或等于50的item,更新restock为true。 |
4. 有读策略"local"的读操作Read2从S3读取 | 读取item A。 |
因果一致性保证 | 描述 |
---|---|
❌读自己的写入 | Read1从S1读取的数据,不会反映Write1之后的状态 |
❌单调读 | Read2从S3读取的数据,不反映Read1之后的状态(即,较早的状态不会反映Read1读取的数据)。)。 |
✅ 单调写 | Write2在Pnew更新的数据,反映Write1之后的状态。 |
❌ 写跟读 | Write2在Pnew更新的数据,不反映Read1之后的状态(即,较早的状态不反映Read1读取的数据)。 |