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引擎则不能支持。
作者简介
张友东,阿里巴巴技术专家,主要关注分布式存储、Nosql数据库等技术领域,先后参与TFS(淘宝分布式文件系统)、Redis云数据库等项目,目前主要从事MongoDB云数据库的研发工作,致力于让开发者用上最好的MongoDB云服务。
写的很棒,学习到了
你好 ,配置文件全局设置slaveOK后,动态设置mongoTemplate.setReadPreference 不生效是怎么一个情况呢
也就是说每次操作,有一个oplog就会增加一个snapshot?
mongo 可以实现正在意义上的读已提交吗?没有延时吗?我现在就出现了,前一个事务提交的数据,后一个事务count不到,写和读都是 major级别
请问一下,我设置了 SecondaryPreferred ,其中一个从节点宕机后, 读请求没有定向到另外一个从节点,这是为什么呢。。。看了好久日志也没找到原因