MongoDB: 通过ReadConcern 来处理备库一致读的问题

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
简介: 问题描述 MongoDB的写请求写入Primary, secondary从Primary自动获取并且应用oplog来保持和主库的同步, MongoDB 允许用户从Primary 或者 secondary 读取数据(由客户端ReadPreference 决定)。

问题描述

MongoDB的写请求写入Primary, secondary从Primary自动获取并且应用oplog来保持和主库的同步, MongoDB 允许用户从Primary 或者 secondary 读取数据(由客户端ReadPreference 决定)。但读数据可能存在以下问题:

  1. 用户从secondary读,但secondary还没有跟上Primary,导致读取了老数据
  2. 用户从primary读到数据,但在该数据复制到secondary 之前crash 了,导致用户”已经“读到的数据”丢失了“。

解决办法

MongoDB 引入了ReadConcern的概念, readConcern 的原型为:

readConcern: {
   level: "[majority|local|linearizable|available|snapshot]",
   afterOpTime: { ts: <timestamp>, term: <NumberLong> },
   afterClusterTime: <timestamp>,
   atClusterTime: <timestamp>
 }

afterOpTime 已经Deprecated 了,可以忽略。
ClusterTime 暂时理解为一个时间点, 而这个时间点是什么含义取决于level 参数。
afterClusterTime 表示在该时间点之后。
atClusterTime 可能是4.2 的全局事务有关,目前driver 也不支持设置。

如果level 为local, ClusterTime 则表示applied oplog time。
如果level 为majority, ClusterTime 则表示大多数节点都达到的 applied oplog time。

为了解决上述问题1, 用户可以指定local + afterClusterTime, 表示用户要数据 ClusterTime 之后的数据(但依然可能不是最新数据),如果这个节点没有,服务器不要返回,一直等到有为止。

为了解决上述问题2, 用户可以指定level 为majority + afterClusterTime, 表示用户要在ClusterTime之后的数据,并且大多数节点都已经有这个数据了。 这时,可以保证即使原来有该数据的节点死掉,是新的primary肯定有这个数据,所有的secondary最终也会有这个数据。

内部实现

每一个MongoDB slave 都维护了一个 _lastAppliedOpTime 和 _lastCommittedOpTime。 (根据mongodb 的配置,_lastAppliedOpTime 有时应该是_durableOpTime,这里不做区分)

_lastAppliedOpTime 表示本节点applied 到了那个时间,
_lastCommittedOpTime 表示复制集内写入到majority节点的_lastAppliedOpTime。

_lastAppliedOpTime 的值比较容易维护,每次apply一批oplog 就会直接更新。_lastCommittedOpTime 更新比较困难。 主要有以下几种方式:

  1. 通过HeartBeat 更新。 MongoDB 中任何一个节点都会和别的节点进行HB, 在每一个HB 的过程中, 发送响应方都会包含自己的_lastAppliedOpTime。 每次处理HeartBeat response的时候, 会去更新 _lastCommittedOpTime。 但HB 的频率默认是2s, 效率比较低。
  2. 每次自己apply 一些oplog 后,借助于HB的结果, 也会改变_lastCommittedOpTime 的值。 所以本节点的_lastAppliedOpTime 比较快,但其他节点的信息还是有可能2s的间隔。
  3. MongoDB 的每一个slave 应用完一批oplog 后, 会立即将自己的_lastAppliedOpTime告诉primary, primary 需要这个信息对writeConcern == ”Majority“ 的客户进行相应。 所以Primary 有比较实时的全局的 _lastCommittedOpTime 信息。 所以各个slave 只要能够及时从primary 那里获得_lastCommittedOpTime 就可以了。 HB 显然不合适,因为间隔太长。 理论上可以放在
    a). slave update 它的_lastAppliedOpTime 到primary 的时候, primary 返回primary 的_lastCommittedOpTime。

b). slave 每次向它的source节点请求oplog 的时候,source节点会返回它的_lastAppliedOpTime。

上述2种方式都比较及时, MongoDB 4.0 中采用了 b 方案。

因为最新的数据不一定是majority 的数据,而对于一个节点来说,用户要的可能是majority 的数据,所以存储引擎需要有一种保存历史数据的能力,目前只有wiredTiger才支持snapshot,所以如果要使用ReadConcern=="majority", 必须使用wiredTiger 引擎。

使用方法

由第一部分我们看到readConcern 需要设置level, afterClusterTime, atClusterTimeout,MongoDB driver 提供了相关的接口让用户自己设置。 其中的Level 用户使用readConcern 则必须指定。其他的则不是必须, driver 会设置默认值。

如果使用了 causally consistent, MongoDB driver会自动更新afterClusterTime 为上一条请求的response 的operationTime。

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
5月前
|
SQL 分布式计算 NoSQL
快速实践: 通过 Flink CDC 一键整库同步 MongoDB 到 Paimon
Apache Paimon (incubating) 是一项流式数据湖存储技术,可以为用户提供高吞吐、低延迟的数据摄入、流式订阅以及实时查询能力。
76587 4
快速实践: 通过 Flink CDC 一键整库同步 MongoDB 到 Paimon
|
5月前
|
NoSQL MongoDB 数据库
DMS(数据传输服务)支持对MongoDB数据库的整库导出
DMS(数据传输服务)支持对MongoDB数据库的整库导出
99 1
|
6月前
|
NoSQL Linux MongoDB
C++库封装mongodb(跨平台开发)
我的初衷是在Linux平台下只提供动态库和头文件,windows平台下提供静态库和头文件给开发者,这个库mongo-proxy对外提供了一些对mongodb的连接,增删改查,创建索引,聚合等操作的封装,开发者只需要关心接口如何调用,而不需要关心接口是如何实现的,也不需要关心mongo-c-driver的相关依赖,这里我抽象出mongo_proxy类,
|
10月前
|
NoSQL MongoDB 数据库
MongoDB 删除数据库
MongoDB 删除数据库
463 0
|
12月前
|
NoSQL MongoDB 数据库
开心档-软件开发入门之MongoDB 删除数据库
【摘要】 本章将会讲解删除当前数据库,默认为 test,你可以使用 db 命令查看当前数据库名。
|
存储 JSON NoSQL
Python 操作mongodb库
MongoDB 是一个基于分布式文件存储的数据库。
209 0
Python 操作mongodb库
|
JSON 运维 NoSQL
服务器又一次被恶意攻击,MongoDB被删库
前几天在自己个人的一台腾讯云服务器上安装了MongoDB,当时着急用,就用的默认配置,端口是默认端口,也没设置密码,还把bind_ip 设置成 0.0.0.0(允许所有ip远程连接)😅,后来就把这事抛到脑后了,也因为经常用无线网卡上网,ip经常是动态的,云服务器的安全组就放开了所有的ip。
服务器又一次被恶意攻击,MongoDB被删库
|
JSON NoSQL Shell
如何将 winston log 库记录的日志写入 mongo DB 数据库
Winston 非常适合配置不同的日志目的地。 在我们的小应用程序中,让我们创建另一个传输。 这次我想把日志保存到一个数据库中,MongoDB 简洁一些。 在 logger.js 文件上,复制以下代码块。 确保安装 Winston MongoDB,即 npm install winston-mongodb。
如何将 winston log 库记录的日志写入 mongo DB 数据库
|
存储 NoSQL MongoDB
MongoDB系统库
本文基于MongoDB社区版 3.4.21、3.6.12、4.0.11,WT引擎,主要是对单节点、副本集、分片的系统库的介绍。
2256 0
|
NoSQL
MongoDB: 通过ReadConcern 达到 snapshot 读的效果
MongoDB 4.0 提供了level == “snapshot” 的readConcern。 该level 的readConcern 本质上和Primary Secondary 无关, 主要解决的问题是: 时间点1: session 1 打开一个cursor 用于读数据时间点2: session 2 修改了 session 1 要读的数据,并且commit 了时间点3: session 1 读到了 session 2 修改的数据。
2314 0