Read Concern "majority"
来自 https://docs.mongodb.com/manual/reference/read-concern-majority/
注意:对于ReadConcern.Majority ,曾经错误理解为会使读取节点落到最新大多数节点,但这个理解应该错误的。ReadConcern.Majority应该不影响定位到哪个节点(ReadPreference会影响节点定位),只是已定位到具体节点的情况下读取该节点的大多数数据。
Example
Consider the following timeline of a write operation Write0 to a three member replica set:
NOTE
For simplification, the example assumes:
-
All writes prior to Write0 have been successfully replicated to all members.
-
Writeprev is the previous write before Write0. (我的标注:在Write0之前的最后一次写入)
-
No other writes have occured after Write0.
Time | Event | Most Recent Write | Most Recent w: “majority” write |
---|---|---|---|
t0 | Primary applies Write0 | Primary: Write0 Secondary1: Writeprev Secondary2: Writeprev | Primary: Writeprev Secondary1: Writeprev Secondary2: Writeprev |
t1 | Secondary1 applies write0 | Primary: Write0 Secondary1: Write0 Secondary2: Writeprev | Primary: Writeprev Secondary1: Writeprev Secondary2: Writeprev |
t2 | Secondary2 applies write0 | Primary: Write0 Secondary1: Write0 Secondary2: Write0 | Primary: Writeprev Secondary1: Writeprev Secondary2: Writeprev |
t3 | Primary is aware of successful replication to Secondary1 and sends acknowledgement to client | Primary: Write0 Secondary1: Write0 Secondary2: Write0 | Primary: Write0 Secondary1: Writeprev Secondary2: Writeprev |
t4 | Primary is aware of successful replication to Secondary2 | Primary: Write0 Secondary1: Write0 Secondary2: Write0 | Primary: Write0 Secondary1: Writeprev Secondary2: Writeprev |
t5 | Secondary1 receives notice (through regular replication mechanism) to update its snapshot of its most recent w: “majority” write | Primary: Write0 Secondary1: Write0 Secondary2: Write0 | Primary: Write0 Secondary1: Write0 Secondary2: Writeprev |
t6 | Secondary2 receives notice (through regular replication mechanism) to update its snapshot of its most recent w: “majority” write | Primary: Write0 Secondary1: Write0 Secondary2: Write0 | Primary: Write0 Secondary1: Write0 Secondary2: Write0 |
Then, the following tables summarizes the state of the data that a read operation with "majority" read concern would see at time T.
image.png(****tang注:t5时间之后 Secondary1****才收到大多数是Write0的信息,读数据才返回Write0)
Read Target | Time T | State of Data |
---|---|---|
Primary | Before t3 | Data reflects Writeprev |
Primary | After t3 | Data reflects Write0 |
Secondary1 | Before t5 | Data reflects Writeprev |
Secondary1 | After t5 | Data reflects Write0 |
Secondary2 | Before or at t6 | Data reflects Writeprev |
Secondary2 | After t6 | Data reflects Write0 |
Storage Engine Support
Read concern "majority" is available for the WiredTiger storage engine.
TIP
The serverStatus command returns the storageEngine.supportsCommittedReads field which indicates whether the storage engine supports "majority" read concern.
MongoDB readConcern 原理解析
MongoDB 可以通过 writeConcern 来定制写策略,3.2版本后又引入了 readConcern 来灵活的定制读策略。
readConcern vs readPreference
MongoDB 控制读策略,还有一个 readPreference 的设置,为了避免混淆,先简单说明下二者的区别。
-
readPreference 主要控制客户端 Driver 从复制集的哪个节点读取数据,这个特性可方便的实现读写分离、就近读取等策略。
-
primary 只从 primary 节点读数据,这个是默认设置
-
primaryPreferred 优先从 primary 读取,primary 不可服务,从 secondary 读
-
secondary 只从 scondary 节点读数据
-
secondaryPreferred 优先从 secondary 读取,没有 secondary 成员时,从 primary 读取
-
nearest 根据网络距离就近读取
-
-
readConcern 决定到某个读取数据时,能读到什么样的数据。
-
local 能读取任意数据,这个是默认设置
-
majority 只能读取到『成功写入到大多数节点的数据』
-
readPreference 和 readConcern 可以配合使用。
readConcern 解决什么问题?
readConcern 的初衷在于解决『脏读』的问题,比如用户从 MongoDB 的 primary 上读取了某一条数据,但这条数据并没有同步到大多数节点,然后 primary 就故障了,重新恢复后 这个primary 节点会将未同步到大多数节点的数据回滚掉,导致用户读到了『脏数据』。
当指定 readConcern 级别为 majority 时,能保证用户读到的数据『已经写入到大多数节点』,而这样的数据肯定不会发生回滚,避免了脏读的问题。
需要注意的是,readConcern 能保证读到的数据『不会发生回滚』,但并不能保证读到的数据是最新的,这个官网上也有说明。
Regardless of the read concern level, the most recent data on a node may not reflect the most recent version of the data in the system.
有用户误以为,readConcern 指定为 majority 时,客户端会从大多数的节点读取数据,然后返回最新的数据。
实际上并不是这样,无论何种级别的 readConcern,客户端都只会从『某一个确定的节点』(具体是哪个节点由 readPreference 决定)读取数据,该节点根据自己看到的同步状态视图,只会返回已经同步到大多数节点的数据。
readConcern 实现原理
MongoDB 要支持 majority 的 readConcern 级别,必须设置replication.enableMajorityReadConcern参数,加上这个参数后,MongoDB 会起一个单独的snapshot 线程,会周期性的对当前的数据集进行 snapshot,并记录 snapshot 时最新 oplog的时间戳,得到一个映射表。
最新 oplog 时间戳 | snapshot | 状态 |
---|---|---|
t0 | snapshot0 | committed |
t1 | snapshot1 | uncommitted |
t2 | snapshot2 | uncommitted |
t3 | snapshot3 | uncommitted |
只有确保 oplog 已经同步到大多数节点时,对应的 snapshot 才会标记为 commmited,用户读取时,从最新的 commited 状态的 snapshot 读取数据,就能保证读到的数据一定已经同步到的大多数节点。
关键的问题就是如何确定『oplog 已经同步到大多数节点』?
primary 节点
secondary 节点在 自身oplog发生变化时,会通过 replSetUpdatePosition 命令来将 oplog 进度立即通知给 primary,另外心跳的消息里也会包含最新 oplog 的信息;通过上述方式,primary 节点能很快知道 oplog 同步情况,知道『最新一条已经同步到大多数节点的 oplog』,并更新 snapshot 的状态。比如当t2已经写入到大多数据节点时,snapshot1、snapshot2都可以更新为 commited 状态。(不必要的 snapshot也会定期被清理掉)
secondary 节点
secondary 节点拉取 oplog 时,primary 节点会将『最新一条已经同步到大多数节点的 oplog』的信息返回给 secondary 节点,secondary 节点通过这个oplog时间戳来更新自身的 snapshot 状态。
注意事项
-
目前 readConcern 主要用于跟 mongos 与 config server 的交互上,参考MongoDB Sharded Cluster 路由策略
-
使用 readConcern 需要配置replication.enableMajorityReadConcern选项
-
只有支持 readCommited 隔离级别的存储引擎才能支持 readConcern,比如 wiredtiger 引擎,而 mmapv1引擎则不能支持。
网友评论