参考 > MongoDB CRUD操作 > MongoDB CRUD概念 > 阅读隔离度,一致性和新近度 > 因果一致性和读写问题
借助MongoDB的因果一致性客户端会话,读写问题的不同组合可提供不同的 因果一致性保证。如果定义因果一致性以表示耐久性,则下表列出了各种组合提供的特定保证:
阅读关注 | 写关注 | 阅读自己的文章 | 单调读 | 单调写 | 写跟读 |
---|---|---|---|---|---|
"majority" |
"majority" |
✅ | ✅ | ✅ | ✅ |
"majority" |
{ w: 1 } |
✅ | ✅ | ||
"local" |
{ w: 1 } |
||||
"local" |
"majority" |
✅ |
如果因果一致性表示持久性,那么从表中可以看出,只有具有"majority"
读关注度的读取操作和具有"majority"
写关注度的写入操作才能保证所有四个因果一致性保证。也就是说,
因果一致的客户端会话只能保证以下方面的因果一致性:
"majority"
关注阅读操作;也就是说,读取操作将返回大多数副本集成员已确认且持久的数据。"majority"
关注写操作 也就是说,写操作要求确认该操作已应用于大多数副本集的有投票权的成员。如果因果一致性并不意味着持久性(即,写操作可能会回滚),则具有写顾虑的写操作也可以提供因果一致性。{ w: 1 }
注意
在某些情况下(但不一定在所有情况下),读和写关注点的其他组合也可以满足所有四个因果一致性保证。
读关注点"majority"
和写关注点
"majority"
确保即使在副本集中的两个成员暂时认为它们是主要的情况下(例如,使用网络分区),这四个因果一致性保证也成立
。尽管两个主数据库都可以完成写操作,但是只有一个主数据库可以完成写操作。{ w: 1 }
"majority"
例如,考虑网络分区划分五个成员副本集的情况:
✅ 自己写 | 读取1从
S 2读取数据,该数据反映了写入1之后的状态。读取2从
S 1读取数据,该数据反映了Write1 1之后是Write 2之后的状态。 |
✅ 单调读 | 读取2从S 3读取数据,该数据反映读取1之后的状态。 |
✅ 单调写道 | 写2更新P 新数据,以反映写1之后的状态。 |
✅ 写跟随读 | 写2更新P 新数据,以反映读1之后的数据状态(即,较早的状态反映读1读取的数据)。 |
方案2(读取关注“多数”和写入关注“多数”)
考虑一个替代序列,其中具有读关注点的读1"majority"
路由到S
1:
序列 | 例 |
---|---|
对于项目
A ,请更新qty 为50 。阅读项目
A 。对于
qty 小于或等于的项目50 ,更新
restock 到true 。阅读项目
A 。 |
按此顺序,直到多数提交点在old上前进,Read 1才返回。直到old和1可以与副本集的其余部分通信时,才会发生这种情况。那时,old
已退出(如果尚未退出),并且两个成员从副本集的其他成员同步(包括Write 1)。P
P
S
P
✅ 自己写 | 读取1反映了写入1 1之后的数据状态,尽管在网络分区已修复并且该成员已与副本集的其他成员进行同步之后。
读取2从
S 3读取数据,该数据反映了Write1 1之后是Write 2之后的状态。 |
✅ 单调读 | 读取2从S 3读取数据,该数据反映读取1之后的状态(即,较早的状态反映在读取1读取的数据中)。 |
✅ 单调写道 | 写2更新P 新数据,以反映写1之后的状态。 |
✅ 写跟随读 | 写2更新P 新数据,以反映读1之后的数据状态(即,较早的状态反映读1读取的数据)。 |
"majority"
和写关注¶{w: 1}
如果因果一致性暗示持久性,则在因果一致性会话中使用读取关注"majority"
和写入关注
可提供以下因果一致性保证:{ w: 1 }
❌读取自己的写入ton单调读取ton单调写入✅写入跟随读取
如果因果一致性并不意味着持久性:
✅读取自己的写入ton单调读取ton单调写入✅写入跟随读取
方案3(“关注多数”和“关注关注” ){w: 1}
在过渡期内有两个初选,因为无论P
老与P
新能满足与写入
的写入关注,一个客户端会话可以成功地发出以下的操作序列,但不是因果关系一致,如果一致因果意味着耐久性:{ w: 1 }
序列 | 例 |
---|---|
对于项目
A ,请更新qty 为50 。阅读项目
A 。对于
qty 小于或等于的项目50 ,更新
restock 到true 。阅读项目
A 。 |
按照这个顺序
P
新的过去写的时候1。P
新的过去写的时间2。➤ 如果因果一致性意味着持久性
❌ 自己写 | 读取1从S 2读取的数据不反映写入1之后的状态。 |
✅ 单调读 | 读取2从S 3读取数据,该数据反映读取1之后的状态(即,较早的状态反映在读取1读取的数据中)。 |
❌ 单调写道 | 写2更新关于P 新数据的数据,该数据不会反映写1之后的状态。 |
✅ 写跟随读 | 写2更新P 新数据,以反映读1之后的状态(即,较早的状态反映读1读取的数据)。 |
➤ 如果因果一致性并不意味着持久性
✅ 自己写 | 读取1从S 2读取数据,返回的数据反映的状态等于Write 1,然后回滚Write 1。 |
✅ 单调读 | 读取2从S 3读取数据,该数据反映读取1之后的状态(即,较早的状态反映在读取1读取的数据中)。 |
✅ 单调写道 | 写2更新P new上的数据,这等效于写1之后回退写1的数据。 |
✅ 写跟随读 | 写2更新P 新数据,以反映读1之后的状态(即,其较早的状态反映读1读取的数据)。 |
方案4(“关注多数”和“关注关注” ){w: 1}
考虑一个替代序列,其中具有读关注点的读1"majority"
路由到S
1:
序列 | 例 |
---|---|
对于项目
A ,请更新qty 为50 。阅读项目
A 。对于
qty 小于或等于的项目50 ,更新
restock 到true 。阅读项目
A 。 |
按此顺序:
S
P
S
P
P
S
➤ 如果因果一致性意味着持久性
❌ 自己写 | 读取1读取的数据不反映已回退的写入1的结果。 |
✅ 单调读 | 读取2从S 3读取数据,该数据反映读取1之后的状态(即,其较早的状态反映读取1读取的数据)。 |
❌ 单调写道 | 写2更新关于P new的数据,该数据不反映写1之后的状态,该状态早于写2但已回滚。 |
✅ 写跟随读 | 写2更新P 新数据,以反映读1之后的状态(即,其较早的状态反映读1读取的数据)。 |
➤ 如果因果一致性并不意味着持久性
✅ 自己写 | 读1返回反映写1最终结果的数据,因为写1最终会回滚。 |
✅ 单调读 | 读取2从S 3读取数据,该数据反映读取1之后的状态(即,较早的状态反映读取1读取的数据)。 |
✅ 单调写道 | 写2更新P new上的数据,这等效于写1之后回退写1的数据。 |
✅ 写跟随读 | 写2更新P 新数据,以反映读1之后的状态(即,较早的状态反映读1读取的数据)。 |
"local"
和写关注¶{w: 1}
在因果一致的会话中使用读关注"local"
和写关注
不能保证因果一致性。{ w: 1 }
❌读取自己的写入ton单调读取ton单调写入❌写入跟随读取
在某些情况下(但不一定在所有情况下),此组合可以满足所有四个因果一致性保证。
方案5(“本地”和“关注”的关注点){w: 1}
在这个短暂的时期,因为无论P
老与
P
新能满足与写入的写入关注,一个客户端会话可以发出以下的操作序列成功,但不是因果关系是一致的:{ w: 1 }
序列 | 例 |
---|---|
对于项目
A ,请更新qty 为50 。阅读项目
A 。对于
qty 小于或等于的项目50 ,更新
restock 到true 。阅读项目
A 。 |
❌自己写 | 读取2从S 3读取数据,该数据仅反映写入2之后的状态,而不反映写入1
之后是写入2的状态。 |
❌单调读 | 读取2从S 3读取数据,该数据不反映读取1之后的状态(即,较早的状态不反映读取1读取的数据)。 |
❌单调写道 | 写2更新关于P 新数据的数据,该数据不会反映写1之后的状态。 |
❌写跟随读 | 写2更新P 新的数据,该数据不反映读1之后的状态(即,较早的状态不反映读1读取的数据)。 |
"local"
和写关注"majority"
¶在因果一致的会话中使用读取关注"local"
和写入关注
"majority"
可提供以下因果一致性保证:
❌读取自己的写入ton单调读取ton单调写入❌写入跟随读取
在某些情况下(但不一定在所有情况下),此组合可以满足所有四个因果一致性保证。
方案6(“关注本地”和“关注多数”)
在此过渡期间,因为只有P
new才能满足 写关注的写操作,所以客户机会话可以成功发出以下操作序列,但因果关系不一致:{ w: "majority" }
序列 | 例 |
---|---|
对于项目
A ,请更新qty 为50 。阅读项目
A 。对于
qty 小于或等于的项目50 ,更新
restock 到true 。阅读项目
A 。 |
❌阅读自己的文章。 | 读1读取数据S 1是WRITE1后没有反映的状态1。 |
❌单调读。 | 读取2从S 3读取数据,该数据不反映读取1之后的状态(即,较早的状态不反映读取1读取的数据)。 |
✅单调写道 | 写2更新P 新数据,以反映写1之后的状态。 |
❌写跟随阅读。 | 写2更新P 新的数据,该数据不反映读1之后的状态(即,较早的状态不反映读1读取的数据)。 |