1.Mongo中的锁机制
意向锁(I锁)
意向锁协议,是一种对树形(层级)资源进行并发控制的协议。目的是为了更好地解决树形逻辑架构应用的并发控制问题。
换句话说,存在意向锁的应用,它的逻辑架构应该为一个树形的逻辑架构。就像mysql、以及今天要说的mongo。通俗来讲,在获取读写锁之前,需要先通过意向锁判定能否获取对应的锁
它由”操作约定”和”冲突矩阵”两部分组成。
mongo中意向锁的操作约定:
- 对一个节点加IX/X锁时,必须先(递归)获取其父节点的IX锁。
- 对一个节点加IS/S锁时,必须先(递归)获取其父节点的IS锁。
常规锁(S锁、X锁、IS锁、IX锁)
- S锁:读锁,对文档加上读锁后,其他事务可以读取该文档,但无法进行对该文档的写操作
- X锁:写锁,对文档加上写锁后,其他事务无法对该文档进行任何操作,只能等待获取锁后,进行操作
- IS锁:意向读锁,对资源存在读取意向,具体互斥原则参考上图
-
2.Mongo中的mvcc机制
mvcc中文全称多版本并发控制, 与mysql中的mvcc实现原理大致一样
目的都是为了读写互不阻塞以及一致性读。因为如果使用锁机制,是完全可以实现不同隔离级别的事务的,但是引入了锁机制以后,读写之间相互阻塞,并发性必然降低。因此才引入了mvcc机制。
在WiredTiger里面, 事务的一致性主要是通过timestamp。
每一个timestamp对应一个相应的snapshot,对于每一个事务操作在事务开始的时候, 得到事务的timestamp,在存储引擎里面会有一个snapshot与之对应。
snapshot记录了该事务在运行过程中的状态, 并且记录了当前事务开始那一时刻,还没有提交的事务的[min_snap_id, max_snap_id],它是从创建snapshot的时候从global_snapshot里面copy出来的。
在min_snap_id之前的事务是已经提交的事务, 对应的数据是可以读的, 大于min_snap_id且小于等于max_snap_id是正在运行事务,不可读
在MongoDB4.0中, 不管有没有显式地调用一个事务, CRUD操作都会默认地给这些操作创建一个事务3.Mongo事务演化路线
MongoDB 3.0版本引入WiredTiger存储引擎之后开始支持事务
- MongoDB 3.6之前的版本只能支持单文档的事务
- MongoDB 4.0版本开始支持副本集部署模式下的事务(此时才算真正意义上的引入了事务的概念)
-
4.单文档事务
MongoDB本身支持的事务类型为单文档事务,也就是说我们对于一个文档的操作是遵循原子性的。
例如执行如下命令:db.test.update({name:”ftc”},{$set:{age:18,name:ftc_king}})
如果在更新了age之后,mongo服务器宕机,那么关于age字段的变更会进行回滚操作。保证了数据的一致性和操作的原子性
在业务允许的情况下,建议使用嵌入式数据模型进行数据的存储。这样可以依赖mongo本身的单文档事务机制,方便的实现属性的原子性变更。一切从简单性出发。如果业务上必须要使用引入式数据模型,也可以使用下文的多文档事务5.多文档事务
MongoDB的多文档事务,包括单一集合的多文档事务,多个集合的多文档事务、多个数据库的多文档事务,甚至是多个分片的多文档事务
mongo的事务是基于session的,每一个session对应。事务的开启,提交,回滚操作都是在一个session(回话)中进行。
java操作代码示例:ClientSession session = client.startSession();
try {
session.startTransaction( ... some tranaction options ... ).build());
// manipulate data
session.commitTransaction();
} catch (MongoCommandException e) {
session.abortTransaction();
} finally {
session.close();
}
同时,对于分布式事务(副本集或分片模式),还需要注意以下参数
writeConcern(写关注)
因为副本集模式写入操作都是写入主节点,因此只需要关注什么时候数据算写入成功了即可
writeConcern 决定一个写操作落到多少个节点上才算成功。writeConcern 的取值包括 0:发起写操作,不关心是否成功;
- 1~${maxInstanceCount}:写操作需要被复制到指定节点数才算成功;
- majority:写操作需要被复制到大多数节点上才算成功。发起写操作的程序将阻塞到写操作到达指定的节点数为止
通常应对重要数据应用 {w: “majority”},普通数据可以应用 {w: 1} 以确保最佳性能。
writeConcern写操作返回耗时受到同步及从节点性能影响,但并不会显著增加集群压力,因此无论是否等待,写操作最终都会复制到所有节点上。设置 writeConcern 只是让写操作等待复制后再返回而已
readPreference(读偏好)
通俗来讲就是从那个节点获取数据,包含以下参数
- primary: 只选择主节点;
- primaryPreferred:优先选择主节点,如果不可用则选择从节点、
- secondary:只选择从节点;
- secondaryPreferred:优先选择从节点,如果从节点不可用则选择主节点;
- nearest:选择最近的节点;
readConcern(读关注)
通俗来讲就是读取什么数据,包含以下参数
- available: 读取所有可用数据;
- local:读取属于当前分片的所有可用数据;
- majority:读取大部分已提交的;
- inearizable:线性读;
- snapshot:快照读;
一般的业务场景来说,设置为majority即可
资料参考:点击这里