Open AurorePaladin opened 2 years ago
writeConcern 决定一个写操作落到多少个节点上才算成功。writeConcern 的取值包括:
发起写操作的程序将阻塞到写操作到达指定的节点数为止。
注意: 默认MongoDB所谓的“写入成功”指写入到节点内存即算成功,无需等待数据真实写入硬盘(落盘)。 journal则定义如何才算成功: true:写操作落到 journal 文件中才算成功 false:写操作到达内存即算成功
注意: 默认MongoDB所谓的“写入成功”指写入到节点内存即算成功,无需等待数据真实写入硬盘(落盘)。
journal则定义如何才算成功:
writeConcern 设置代码格式如下:
{ writeConcern:{w:"majority",j:true,wtimeout:3000} }
在读取数据的过程中,我们需要关注以下两个问题:
第一个问题是由 readPreference 来解决。 第二个问题是由 readConcern 来解决。
readPreference 决定使用哪个节点来满足正在发起的请求。 可选值包括:
用户下订单后马上将用户转到订单详情页——primary/primaryPreferred。 因为此时从节点可能还没复制到新订单。
用户查询自己下过的订单——secondary/secondaryPreferred。查询历史订单时效性通常没有太高要求。
生成报表——secondary。报表对时效性要求不高,但资源需求大,可以在从节点上单独处理,避免对线上用户造成影响。
将用户上传的图片分发到全世界,让各地用户能够就近读取——nearest。 每个地区的应用选择最近的节点读取数据。
readPerference 只能控制使用一类节点,Tag 则可以将节点选择控制到一个或几个节点。
考虑以下场景:
可以使用 Tag 来达到这样的控制目的:
有3种配置方式:
第1种:通过 MongoDB 的连接串参数 mongodb://host1:27107,host2:27107,host3:27107/?replicaSet=rs&readPreference=secondary
第2种:通过 MongoDB 的驱动程序 API MongoCollection.withReadPreference(ReadPreference readPref)
第3种:Mongo Shell db.collection.find({}).readPref("secondary")
指定 readPreference 时也应注意高可用问题。例如将 readPreference 指定 primary,则发生故障转移不存在 primary 期间将没有节点可读。 如果业务允许,则应选择 primaryPerferred。
使用 Tag 时也会遇到同样的问题,如果只有一个节点拥有一个特定 Tag,则在这个节点失效时将无节点可读。 这在偶时候是期望的结果,有时候不是。 例如:
在 readPreference 选择了指定的节点后,readConcern 决定这个节点上的数据哪些是可读的,类似于关系数据库的隔离级别。
可选值包括:
在复制集中 local 和 available 是没有区别的。两者的区别主要体现在分片集上。
注意事项:
虽然看上去总是应该选择 local,但毕竟对结果集进行过滤会造成额外消耗。 在一些无关紧要的场景(例如统计)下,也可以看考虑 available。
MongoDB <= 3.6版本,不支持对从节点使用 {readComcern:"local"}
从主节点读取数据时默认 readConcern 是 local,从从节点读取数据时默认 readConcern 是 available(向前兼容原因)。
节点上维护多个 x 版本,MVCC机制: MongoDB通过维护多个快照来链接不同的版本,每个被大多数节点确认过的版本都将是一个快照,快照持续到没有人使用为止才被删除。
MongoDB 中的回滚:
所以从分布式系统的角度来看,事务的提交被提升到了分布式集群的多个节点级别的“提交”,而不再是单个节点上的“提交”。
在可能发生回滚的前提下考虑脏读问题:
使用{readConcern:"majority"} 可以有效避免脏读。
场景举例:向主节点写入一条数据,立即从从节点读取这条数据。
如何保证自己能读到刚刚写入的那条数据?
答:使用 writeConcern + readConcern majority来解决。
db.orders.insert({oid:34},{writeConcern:{w:"majority")}); db.orders.find({oid:34}).readPref("secondary").readConnern("majority");
只读取大多数节点确认过的数据。和 majority 最大差别是保证绝对的操作现行顺序。
注意: 只对读取单个文档时有效 可能导致非常慢的时候,因此总是建议配合使用 maxTimeMS。
注意:
{readConcern:"snapshot"} 只在多文档事务中生效。将一个事务的 readConcern 设置为 snapshot,将保证在事务中的读:
因为所有的读都将使用同一个快照,直到事务提交为止该快照才被释放。
MongoDB 4.2 开始全面支持了多文档事务,但并不代表大家应该毫无节制地使用它。相反,对事务的使用原则应该是:能不用尽量不用。
通过合理地设计文档模型,可以规避绝大部分使用事务的必要性。
为什么? 事务=锁、节点协调,额外开销,性能影响。
多行,多表,多文档 一次性完成或都一个也不完成(数据回滚),这就叫 多文档事务。
事务完成前,事务外的操作对该事务所做的修改不可访问。
如果事务内使用 {readConcern:"snapshot"},则可以达到可重复读 Repeatable Read。
MongoDB 的事务错误处理机制不同于关系数据库:
当一个事务开始后,如果事务要修改的文档在事务外部被修改过,则事务修改这个文档时会触发 Abort 错误,因为此时的修改冲突了。
这种情况下,只需要简单地重做事务就可以了。
如果一个事务已经开始修改一个文档,在事务以外尝试修改同一个文档,则事务外的修改会等待事务完成后才能继续进行。
MongoDB事务开发
写事务
什么是 writeConcern ?
writeConcern 决定一个写操作落到多少个节点上才算成功。writeConcern 的取值包括:
发起写操作的程序将阻塞到写操作到达指定的节点数为止。
writeConcern 设置代码格式如下:
注意事项
读数据
在读取数据的过程中,我们需要关注以下两个问题:
第一个问题是由 readPreference 来解决。
第二个问题是由 readConcern 来解决。
readPreference
readPreference 决定使用哪个节点来满足正在发起的请求。
可选值包括:
readPreference 场景举例
用户下订单后马上将用户转到订单详情页——primary/primaryPreferred。 因为此时从节点可能还没复制到新订单。
用户查询自己下过的订单——secondary/secondaryPreferred。查询历史订单时效性通常没有太高要求。
生成报表——secondary。报表对时效性要求不高,但资源需求大,可以在从节点上单独处理,避免对线上用户造成影响。
将用户上传的图片分发到全世界,让各地用户能够就近读取——nearest。 每个地区的应用选择最近的节点读取数据。
readPerference 与 Tag
readPerference 只能控制使用一类节点,Tag 则可以将节点选择控制到一个或几个节点。
考虑以下场景:
可以使用 Tag 来达到这样的控制目的:
如何配置 readPreference ?
有3种配置方式:
第1种:通过 MongoDB 的连接串参数
mongodb://host1:27107,host2:27107,host3:27107/?replicaSet=rs&readPreference=secondary
第2种:通过 MongoDB 的驱动程序 API
MongoCollection.withReadPreference(ReadPreference readPref)
第3种:Mongo Shell
db.collection.find({}).readPref("secondary")
注意事项
指定 readPreference 时也应注意高可用问题。例如将 readPreference 指定 primary,则发生故障转移不存在 primary 期间将没有节点可读。 如果业务允许,则应选择 primaryPerferred。
使用 Tag 时也会遇到同样的问题,如果只有一个节点拥有一个特定 Tag,则在这个节点失效时将无节点可读。 这在偶时候是期望的结果,有时候不是。 例如:
什么是 readConcern ?
在 readPreference 选择了指定的节点后,readConcern 决定这个节点上的数据哪些是可读的,类似于关系数据库的隔离级别。
可选值包括:
available 和 local
在复制集中 local 和 available 是没有区别的。两者的区别主要体现在分片集上。
考虑以下场景:
注意事项:
虽然看上去总是应该选择 local,但毕竟对结果集进行过滤会造成额外消耗。 在一些无关紧要的场景(例如统计)下,也可以看考虑 available。
MongoDB <= 3.6版本,不支持对从节点使用 {readComcern:"local"}
从主节点读取数据时默认 readConcern 是 local,从从节点读取数据时默认 readConcern 是 available(向前兼容原因)。
majority 的实现方式
节点上维护多个 x 版本,MVCC机制:
MongoDB通过维护多个快照来链接不同的版本,每个被大多数节点确认过的版本都将是一个快照,快照持续到没有人使用为止才被删除。
majority 与 脏读(读到的数据是不干净的)
MongoDB 中的回滚:
所以从分布式系统的角度来看,事务的提交被提升到了分布式集群的多个节点级别的“提交”,而不再是单个节点上的“提交”。
在可能发生回滚的前提下考虑脏读问题:
使用{readConcern:"majority"} 可以有效避免脏读。
如何实现安全的读写分离
场景举例:向主节点写入一条数据,立即从从节点读取这条数据。
如何保证自己能读到刚刚写入的那条数据?
答:使用 writeConcern + readConcern majority来解决。
linearizable
只读取大多数节点确认过的数据。和 majority 最大差别是保证绝对的操作现行顺序。
snapshot
{readConcern:"snapshot"} 只在多文档事务中生效。将一个事务的 readConcern 设置为 snapshot,将保证在事务中的读:
因为所有的读都将使用同一个快照,直到事务提交为止该快照才被释放。
多文档事务
MongoDB 4.2 开始全面支持了多文档事务,但并不代表大家应该毫无节制地使用它。相反,对事务的使用原则应该是:能不用尽量不用。
通过合理地设计文档模型,可以规避绝大部分使用事务的必要性。
为什么?
事务=锁、节点协调,额外开销,性能影响。
MongoDB ACID 多文档事务支持
多行,多表,多文档 一次性完成或都一个也不完成(数据回滚),这就叫 多文档事务。
事务的隔离级别
事务完成前,事务外的操作对该事务所做的修改不可访问。
如果事务内使用 {readConcern:"snapshot"},则可以达到可重复读 Repeatable Read。
事务写机制
MongoDB 的事务错误处理机制不同于关系数据库:
当一个事务开始后,如果事务要修改的文档在事务外部被修改过,则事务修改这个文档时会触发 Abort 错误,因为此时的修改冲突了。
这种情况下,只需要简单地重做事务就可以了。
如果一个事务已经开始修改一个文档,在事务以外尝试修改同一个文档,则事务外的修改会等待事务完成后才能继续进行。
注意事项: