MongoDB爱好者
垂直技术交流平台

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 只能读取到『成功写入到大多数节点的数据』

readPreferencereadConcern 可以配合使用。

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云服务。

赞(23)
未经允许不得转载:MongoDB中文社区 » MongoDB readConcern 原理解析

评论 8

评论前必须登录!

 

  1. #1

    写的很棒,学习到了

    rudonx_20204年前 (2020-04-28)
  2. #2

    你好 ,配置文件全局设置slaveOK后,动态设置mongoTemplate.setReadPreference 不生效是怎么一个情况呢

    mongo_xiaobai4年前 (2020-05-13)
  3. #3

    也就是说每次操作,有一个oplog就会增加一个snapshot?

    neyzoter4年前 (2020-06-12)
  4. #4

    mongo 可以实现正在意义上的读已提交吗?没有延时吗?我现在就出现了,前一个事务提交的数据,后一个事务count不到,写和读都是 major级别

    guohu4年前 (2020-08-18)
  5. #5

    请问一下,我设置了 SecondaryPreferred ,其中一个从节点宕机后, 读请求没有定向到另外一个从节点,这是为什么呢。。。看了好久日志也没找到原因

    shuaib2年前 (2022-03-15)