前言:mongodb作为一个分布式系统在实际应用中也要面临着访问延迟和一致性之间的权衡。好在mongodb的一致性模型的对读写操作的访问延迟和一致性提供了丰富的选项。

1、write-concern:确认改操作已应用于复制集中大多数成员(准确说是可投票成员);
2、read-concern:数据已被大多数复制集成员确认并且持久化的(默认应该是majority);

3、read-preference: 对于replica set,是返回当前节点的最新数据,还是返回写入节点最多的数据,还是根据一些函数计算出的数据。

https://docs.mongodb.com/manual/reference/read-concern/

https://docs.mongodb.com/manual/reference/write-concern/

https://docs.mongodb.com/manual/core/replica-set-write-concern/

https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/

一、写关注(write concern)

写关注描述了mongodb针对单机mongod或者副本集或分支集群的写操作的ack级别。对于分片集群,mongos实例会把写关注透传到shards。

从4.4版本开始,副本集和分片集群支持设置全局默认的write concern。没有显示指定写关注的操作将继承全局默认的设置。有关更多信息,参考 setDefaultRWConcern.

对于某些应用程序来说,写关注十分重要。它能判断哪些写操作成功写入了,哪些失败了。这样对于失败操作,驱动程序能返回错误,由应用程序决定怎么处理。如果没有写关注,应用程序发送一个写操作到socket后剩下的就完全不管了,至于后面发生了什么、是否写入数据库统统不关心;显然对于很多应用这样是不行的。带有写关注的操作会等到数据库确认成功写入后才能返回,因此写关注会带来性能上的损失。

1、写关注的指定

写关注包括如下字段:

 { w: <value>, j: <boolean>, wtimeout: <number> }

(1)w选项:确认写入操作的请求已传播到指定数量的mongod实例或具有指定标记的mongod实例。

①w: majority——写操作传播到数据承载节点的calculated majority成员;这里举个例子,对于一个3节点的副本集(Primary-Secondary-Secondary,P-S-S),它的calculated majority是2,即写操作必须传播到primary和一个secondary同ack写关注给client。

②w: number——写操作传播到指定数量的mongod实例;当取值为0时,驱动程序不会使用写关注,只返回网络和socket的错误。当取值为1时,驱动程序使用写关注,但是只针对primary节点,这个配置项是对于复制集或单mongod实例默认写关注配置。当取值为整数且大于1时,写关注将针对复制集中的n个节点,当客户端收到这些节点的反馈信息后,命令才返回给客户端继续执行。

(2)j选项:设为1表示确认写入操作的请求已经写入磁盘日志(on-disk journal),也就是下一次Journal log提交。这种情况是可以容忍服务器突然宕机,断电等意外情况的数据恢复的。

(3)wtimeout选项:指定时间限制,以防止写操作无限期阻塞。

下图是一个配置了"w:2"的写关注执行流程图,一个是primary节点,一个是secondary节点。

一个需要写关注ack的应用程序发出一个写操作后会等待直到primary节点接受必要数量节点的相应写关注ack。对于w选项为“majority”或者w大于1来说,primary节点会一直等待直到必要数量的secondary作出ack才会返回写关注ack。对于w为1的写关注,primary节点可以在本地写操作完成后立即返回写关注确认(ack)。

确认写操作的成员越多,因为主操作失败而导致的回滚的可能性就越小。但是指定更高的写入关注会带来延迟增加的问题,因为客户端必要等待直到它收到指定级别的写关注确认(ack)。

2、验证副本集的写关注

下面的操作是包括写关注选项的insert方法。操作指定w为"majority"此外还指定wtimeout为5秒以防止操作被无限期的阻塞。

db.products.insert({ item: "envelopes", qty : 100, type: "Clasp" },{ writeConcern: { w: "majority" , wtimeout: 5000 } }
)

二、读关注(read concern)

read concern 是针对读操作的配置。它控制读取数据的"新鲜度"和持久性。read concern 选项控制数据读取的一致性,分为 local、available、majority、linearizable 四种,它们对一致性的承诺依次由弱到强。其中 linearizable 表示线性一致性,另外 3 种级别代表了 MongoDB 在实现最终一致性时,对访问延迟和一致性的取舍。
(1)local/available: 语义基本一致,都是读操作直接读取本地最新的数据,但不保证该数据已被写入大多数复制集成员。数据可能会被回滚。默认是针对主节点读。 如果读取操作与因果一致的会话相关联,则针对副节点读。唯一的区别在于,avaliable 在分片集群场景下,为了保证性能,可能返回孤儿文档。

(2)majority:读取 majority committed 的数据,可以保证读取的数据不会被回滚,但是并不能保证读到本地最新的数据。受限于不同节点的复制进度,可能会读取到更旧的值。当写操作对应的 write concern 配置中 w 的值越大,则写操作在扩散到更多的复制集节点上之后才返回写成功,这时通过 read concern 被配置为 majority 的读操作进行读取数据,就有更大的概率读取到最新的数据。
(3)linearizable:读取 majority committed 的数据,但会等待在读之前所有的 majority committed 确认。它承诺线性一致性,要求读写顺序和操作真实发生的时间完全一致,既保证能读取到最新的数据,也保证读到数据不会被回滚。只对读取单个文档时有效,且可能导致非常慢的读,因此总是建议配合使用 maxTimeMS 使用。linearizable 只能用在主节点的读操作上,考虑到写操作也只能发生在主节点上,相当于说 MongoDB 的线性一致性被限定在单机环境下实现。实现 linearizable,读取的数据应该是被 write concern 为 majority 的写操作写入到 MongoDB 集群中的、且持久化到日志中的数据。如果数据写入到多数节点后,没有在日志中持久化,当这些节点发生重启恢复,那么之前通过配置 read concern 为 linearizable 的读操作读取到的数据就可能丢失。可以通过 writeConcernMajorityJournalDefault 选项保证指定 write concern 为 majority 的写操作在日志中是否持久化。如果写操作持久化到了日志中,但是没有复制到多数节点,在重新选主后,同样可能会发生数据丢失,违背一致性承诺。

snapshot: 与关系型数据库中的快照隔离级别语义一致。最高隔离级别,接近于 serializable。是伴随着 MongoDB 4.0 版本中新出现的多文档事务而设计的,只能用在显式开启的多文档事务中。如果事务是因果一致会话的一部分,且 write concern 为 majority,则在事务提交后,读操作可以保证已从多数提交数据的快照中读取,该快照提供与该事务开始之前的操作的因果一致性。它读取 majority committed 的数据,但可能读不到最新的已提交数据。snapshot 保证在事务中的读不出现脏读、不可重复读和幻读。 因为所有的读都将使用同一个快照,直到事务提交为止该快照才被释放

思考

当负载过大时mongodb的主从同步存在明显延迟,导致有时候读取不到最新的数据。

正常使用场景这种延迟几乎是感知不到的,但是如果写入负载特别大的情况下可能就会出现较为明显的延迟。关于这一点也不是mongodb的问题,因为CAP理论决定了一致性、可用性和分区容忍性只能3取2,对于大多数据分布式数据库来说都是选择A(可用性)和P(分区容忍性)放弃C(一致性),转而追求要求不那么高的最终一致性。

注:CAP理论中的C(一致性)指的是线性一致性(强一致性),与之对应的是最终一致性(弱一致性)。

也就是说无论负载如何,延迟都是一定存在的,只是时间长短的问题。如果你的业务非常强依赖这里的一致性,那么一定要在逻辑上进行保证而不是把希望寄托于“运气”或负载高低之上。

当程序写完数据开始读的时候,要确保从库已经复制到了刚才写入的数据,现显然这里不能依赖于程序sleep。正确的做法是{w,"majority"},即写的时候要阻塞到写操作已经对大多数节点生效才算完成。但是仅有这一点是不够的,因为你并不能保证你所读取的节点正好在“大多数”之内。既然如此可不可以用{w,3}呢(这假设3副本)?除去阻塞时间更长以外,更为致命的问题是强依赖与所有节点了,其中任何一个节点失败(例如宕机)就会导致所有节点都失败。所以更合理的做法是{readConcern:"majority"},其语义是大多数节点有就算有。至此,我们得到了如下方案:

{w: "majority"} + {readConcern: "majority"}

然后你就会发现新的问题。那便是你做任何一件事情都需要集群中的大多数成员承认,也就是说一个请求要扩散到大所属节点之上。显然这违背了分散压力的初衷(甚至于压力更大了)。但是没有办法:“天下没有免费的午餐”。 但是副本集很重要的一个作用就是分散压力(读写分离);究竟是只读primary保证线性一致性 还是 适当的放弃一致性确保高性能 就需要结合具体业务场景来考虑了。

15.MongoDB的一致性(读关注与写关注)相关推荐

  1. oracle写会阻塞读吗,一致性读保证了读不阻塞写

    再深入一步,为大家测试下,如果手动将buffer Header中Buffer Pin内存位设置为1,这就等同于加上了共享Buffer Pin锁,这时另开一个会话,更新这个块,会有什么情况呢? 1.取T ...

  2. 补偏救弊 | 关于一致性读与语句性能关系的一大误区

    作者简介 黄玮(Fuyuncat) 资深 Oracle DBA,致力于数据库底层技术的研究,其作品获得广大同行的高度评价. 个人网站 www.HelloDBA.com 研究背景 实际上,我们所说的保证 ...

  3. Oracle 物理读 逻辑读 一致性读 当前模式读总结浅析

    Oracle 物理读 逻辑读 一致性读 当前模式读总结浅析 在ORACLE数据库中有物理读(Physical Reads).逻辑读(Logical Reads).一致性读(Consistant Get ...

  4. MySQL怎么运行的系列(十一)快照读、锁定读、半一致性读 和 加锁语句分析

    本系列文章目录 展开/收起 MySQL怎么运行的系列(一)mysql体系结构和存储引擎 MySQL怎么运行的系列(二)Innodb缓冲池 buffer pool 和 改良版LRU算法 Mysql怎么运 ...

  5. 使用ReaderWriterLock类实现多用户读/单用户写同步

    使用ReaderWriterLock类实现多用户读/单用户写同步[1] 2015-03-12 应用程序在访问资源时是进行读操作,写操作相对较少.为解决这一问题,C#提供了System.Threadin ...

  6. oracle 一致性读数量,ORACLE 一致性读原理记录

    什么是一致性读? 一致性读指的是在从查询那一刻起,中间的变化不予理会. 举例说明 比如我有两个帐户A,B. A 有1000块,B有1000快.我查询的时候查询速度比较慢.中间A转500到B账户. 已经 ...

  7. mysql 一致性读_mysql/mariadb知识点总结(27):一致性读,快照读

    在本博客中,"mysql"是一个系列文章,这些文章主要对mysql/mariadb的常用知识点进行了总结,每一篇博客总结的知识点有所不同,具体内容可参考mysql文章列表. mys ...

  8. Oracle一致性读(Consistent Read)的原理

    在Oracle数据库中,undo主要有三大作用: 提供一致性读(Consistent Read).回滚事务(Rollback Transaction)以及实例恢复(Instance Recovery) ...

  9. Oracle数据库一致性读的原理

    在Oracle数据库中,undo主要有三大作用:提供一致性读(Consistent Read).回滚事务(Rollback Transaction)以及实例恢复(Instance Recovery) ...

  10. mysql mvcc undo_Mysql Innodb中undo-log和MVCC多版本一致性读 的实现

    http://blog.sina.com.cn/s/blog_4673e603010111ty.html 本文主要介绍mysql中innodb引擎undo-log和事务中MVCC多版本一致性读的实现. ...

最新文章

  1. Message、Handler、Message Queue、Looper之间的关系
  2. 预处理器Less和Sass
  3. QT的QDBusPendingReply类的使用
  4. Java-Calendar
  5. Android添加单元测试的方法与步骤
  6. WordPress自动采集插件:WP-CTspider(长腿蜘蛛)
  7. 设计师不可错过的2022年设计类网站大搜集,素材 教程 案例 全都有
  8. 小米手机文件 ftp服务器,小米手机与电脑FTP连接(不用每一次都输入ftp地址)...
  9. 用python 打开qq自动输入账号密码登陆 (python3 案例1)
  10. VMWare Fusion 安装 Centos7操作系统
  11. C++三种方法求解两个数最大公因数和最小公倍数
  12. 【独行秀才】macOS Monterey 12.1Beta4(21C5045a)原版镜像
  13. pip install时报错超时(pip._vendor.requests.packages.urllib3.exceptions.ReadTimeoutError: HTTPSConnec)解决方案
  14. eclipse jvm_一种与众不同的JVM语言:Eclipse Xtend(现在适用于Android)
  15. Python编曲实践(八):我,乔鲁诺·乔巴那,能用两百行代码写出JOJO黄金之风里我自己的出场曲!
  16. uiautomation(如何)实现自动化
  17. 关于Rost ContentMining 6.0情感分析出现空白的解决方案
  18. 转炉炼钢计算机仿真实验报告,计算机仿真、实验报告.docx
  19. mysql 留存率_mysql查询用户留存语法(用户留存和用户留存率问题)
  20. 肯塔基大学计算机科学,2020年肯塔基大学排名TFE Times美国最佳计算机科学硕士专业排名第114...

热门文章

  1. Android Framework 全面分析 BootAnimation
  2. L1-023 输出GPLT (20 分) — 团体程序设计天梯赛
  3. 十大算法 — 选择排序法【C语言代码诠释】
  4. rk3399_secureboot在linux环境中操作说明
  5. Jedis 常用API使用
  6. 判断数据类型的几种办法
  7. AngularJS——第8章 服务
  8. 使用Python写的第一个网络爬虫程序
  9. 解决 jersey 单jar包 IME media type text/plain was not found.
  10. 【网络流24题】No.4 魔术球问题 (二分+最小路径覆盖)