前面我们学习了在MongoDB中的写操作事务管理,本篇我们开始进入读操作事务。
在通过mongodb读取数据时,我们一般需要关注两个问题:
(1)从哪里读取?
(2)什么样的数据可以被读取?
第一个问题,mongodb交给了 readPreference 来解决。
第二个问题,mongodb交给了 readConcern 来解决。
readPreference 选项决定了mongodb会使用哪一个节点(primary or secondary)来满足正在发起的读请求。
readPreference的可选值:
(1)primary:只选择主节点
(2)primaryPreferred:优先选择主节点,如果主节点不可用则选择从节点
(3)secondary:只选择从节点
(4)secondaryPreferred:优先选择从节点,如果从节点不可用则选择主节点;
(5)nearest:选择最近的节点,无论是primary 还是 secondary 角色。
比如,在电商场景中,用户下单后会立即跳转到订单详情页,这时候可以配置为 primary/primaryPreferred,因为这时候从节点可能还没有获得同步的新订单数据。
又如,用户查询自己的历史订单记录,这时候可以配置为 secondary/secondaryPreferred,因为这时候用户对历史订单的时效性没有太高的要求;
再如,在Report场景中,由于报表对时效性的要求并不是太高,但资源需求较大,因此可以配置为secondary,在从节点单独处理,避免对线上用户造成影响。
最后,在内容管理场景中,将用户上传的图片分发到全世界(比如多数据中心的场景),可以配置为nearest,让各地用户能够就近读取数据。
在MongoShell中:
// 在读取students集合时采用secondary策略 db.teams.find().readPref("secondary");
在.NET应用程序中:
// 通过连接字符串设置全局默认readPreference策略 mongodb://mongo-master:27017,mongo-slave1:27017,mongo-slave2:27017/?replicaSet=testRs&readPreference=secondary // 在.NET代码中通过Driver设置readPreference策略 _contacts.WithReadPreference(ReadPreference.Secondary).Find(doc => true);
在mongodb中,readPreference只能控制某一类节点,而tag则可以将节点选择控制到一个或多个节点。在Kubernetes中,也有tag这个概念,可以将容器部署到指定tag的一个或多个node上。
比如,一个5节点的复制,3个节点硬件较好专供服务线上客户实时操作,2个节点硬件较低专供服务报表生成。这时,就可以给3个硬件较好的节点打tag:{purpose: "online"},给2个硬件较低的节点打tag:{purpose: "reporting"}。最后,在线应用在读取数据时就可以指定tag是{purpose: "online"}的节点读取,而报表应用则指定tag是{purpose: "reporting"}的节点读取。
readPreference 解决了从哪里读数据的问题,而readConcern 则用来解决什么样的数据可以被读取。
readConcern 类似于传统关系型数据库的隔离级别,它的可选项包括:
(1)available:读取所有可用的数据
(2)local:读取所有可用且属于当前分片的数据
(3)majority:读取在大多数节点上提交完成的数据
(4)linearizable:可线性化读取文档
(5)snapshot:读取最近快照中的数据
可以看到,上面的可选项基本上也是遵循了关系型数据的事务隔离级别,越往下安全性越高,但是并发性能越低。
在实际应用中,和 writeConcern 一样,我们往往会折中考虑做权衡,因此我们在readConcern的一般性场景下选择 majority,即读取在大多数节点上提交完成的数据。如果跟事务隔离级别对比,那majority就类似于Read Commited即提交读。
在MongoShell中:
db.teams.find().readConcern("majority");
在.NET应用程序中:
// 连接字符串全局设置 mongodb://mongo-master:27017,mongo-slave1:27017,mongo-slave2:27017/?replicaSet=testRs&readConcern=majority // 在.NET代码中操作Driver时设置 _contacts.WithReadConcern(ReadConcern.Majority).Find(doc => true);
在某些场景中,向主节点写入了一条数据,立即从从节点读取这条数据,如何才能在不改变方式的前提下保证用户能够读取到刚刚写入的数据?
对于这类问题,我们也可以称其为如何实现一个安全的读写分离。
这里,我们可以使用 writeConcern + readConcern 的组合拳来实现:
db.orders.insert({ oid: 101, sku: "kiteboar", q: 1}, {writeConcern:{w: "majority”}}); db.orders.find({oid:101}).readPref("secondary").readConcern("majority");
本文简单介绍了MongoDB的读操作事务,它有两个重要的参数 readPreference 和 readConcern,分别解决从哪里读取数据 和 什么样的数据可以被读取 的问题。
下一篇,我们会学习MongoDB的多文档事务,这是MongoDB 4.x版本的最大改进。
唐建法,《MongoDB高手课》(极客时间)
郭远威,《MongoDB实战指南》(图书)
△推荐订阅学习
作者:周旭龙
出处:https://edisonchou.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。