MongoDB 单文档原生支持原子性,也具有事务的特性,可是咱们提及事务,一般是指在多文档中的实现,所以,MongoDB 在 4.0 版本支持了多文档事务,4.0 对应于复制集的多表、多行,后续又在 4.2 版本支持了分片集的多表、多行事务操做。mongodb
原子性(Atomicity):事务必须是原子工做单元,对于其数据修改,要么全执行,要么全不执行。相似于 Redis 中我一般使用 Lua 脚原本实现多条命令操做的原子性。shell
一致性(Consistency):事务在完成时,必须使全部的数据都保持一致状态。bash
隔离性(Isolation):由并发事务所作的修改必须与任何其余并发事务所做的修改隔离(简而言之:一个事务执行过程当中不该受其它事务影响)。并发
持久性(Durability):事务完成以后,对于系统的影响是永久性的。异步
在事务操做中会分别使用到 readConcern、writeConcern、readPreference 这几个选项,用于控制 Session 的行为,下面分别予以介绍。性能
事务使用事务级别的 writeConcern 来提交写操做,决定一个事务的写入成功与否要看 writeConcern 选项设置了几个节点,默认状况下为 1。测试
writeConcern: {
w:"majority" // 大多数原则
j:true,
wtimeout: 5000,
}
复制代码
JavaScript 使用示例:ui
db.user.insert({name: "Jack"}, {writeConcern: {w: "majority"}})
复制代码
对于重要数据能够应用 w:"majority" 设置,普通数据 w:1 设置则能够保证性能最佳,w 设置的节点数越多,等待的延迟也就越大,若是 w 等于总节点数,一旦其中某个节点出现故障就会致使整个写入失败,也是有风险的。spa
docs.mongodb.com/manual/refe…rest
在一个事务操做中使用事务级别的 readPreference 来决定读取时从哪一个节点读取。可方便的实现读写分离、就近读取策略。
mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017,mongodb2.example.com:27017/admin?replicaSet=myRepl&readPreference=secondary
复制代码
db.collection.find({}).readPref( "secondary")
复制代码
测试时可借助 db.fsyncUnlock() 对从节点锁定,仅主节点写入数据。
MongoDB 3.2 引入了 readConcern 来决定读取的策略,可是与 readPreference 不一样,readPreference 决定从哪一个节点读取,readConcern 决定该节点的哪些数据是可读的。主要保证事务中的隔离性,避免脏读。
在启动 Mongod 实例时,指定 --enableMajorityReadConcern 选项或在配置文件中配置
enableMajorityReadConcern=true
复制代码
从新启动实例,Mongo shell 登录实例,使用 db._adminCommand( {getCmdLineOpts: 1})
查看配置,能够看到在复制集中会多出一个配置,说明配置成功。
{
...
"parsed" : {
"replication" : {
"enableMajorityReadConcern" : true, // 变化之处
"oplogSizeMB" : 1024,
"replSet" : "May"
},
},
}
复制代码
db.user.find().readConcern("majority")
复制代码
MongoDB 的 readConcern 默认状况下是脏读,例如,用户在主节点读取一条数据以后,该节点未将数据同步至其它从节点,就由于异常挂掉了,待主节点恢复以后,将未同步至其它节点的数据进行回滚,就出现了脏读。
readConcern 级别的 majority 能够保证读到的数据已经落入到大多数节点。因此说保证了事务的隔离性,所谓隔离性也是指事务内的操做,事务外是看不见的。
一个典型的应用场景是用户写入订单数据(数据写入 Primary),当即调用查询接口,因为采用读写分离模式,连接字符串设置 readPreference=secondaryPreferred 订单写入主节点以后并不能保证数据当即同步到从节点,若此时直接由从节点读取数据, 偶尔会出现订单数据没法找到,用户就会感受很奇怪,明明下了订单,却又查找不到,形成一些异常订单。
一种致使下单以后再次查找丢失订单的的写法以下:
db.order.insert({"id": "123456789"})
db.order.find({"id": "123456789"}).readPref("secondaryPreferred")
复制代码
解决方法一:
设置 readPreference=primary,将复制集的节点读取由从节点转换为主节点。
这种方式一个缺点是数据量大了以后会增长主节点的压力,也就没有了主从分离的模式。
解决方法二:
使用 writeConcern、readConcern 组合来解决,即保证读写分离模式,也保证了数据的一致性。
// 写入时保证大多数节点写入成功
db.order.insert({"id": "123456789"}, {writeConern: {w: "majority"}})
// 读取时保证这条数据已在大多数节点存在
db.order.find({"id": "123456789"}).readPref("secondaryPreferred").readConcern("majority")
复制代码
本文是对 MogoDB 事务的一个初步了解,Read Concern/Write Concern/Read Preference 这些在后续事务实践中都会应用,但愿你们能够事先进行一个了解,在接下来的一篇文章中,我会介绍 MongoDB 的事务应该如何应用,同时结合 Node.js 进行实践说明,欢迎关注!