MongoDB的锁机制
在MongoDB里面有如下4中锁:
锁 | 描述 |
S | 读操作的共享锁 |
IS | 意向读操作共享锁 |
X | 排它的写锁 |
IX | 意向的排它写锁 |
MongoDB 锁的兼容矩阵:
IS | IX | S | X | |
IS | yes | yes | yes | no |
IX | yes | yes | no | no |
S | yes | no | yes | no |
X | no | no | no | no |
MongoDB使用多粒度锁来锁定资源, 它允许我们按照全局的, 库级的或者集合级的方式锁定资源。MongoDB使用读写锁来允许对一个共享的资源并发的进行读, 比如一个库或者集合。除了一个共享模式读锁(S)和一个排它锁(X), 意向读锁(IS)和意向写锁(IX)表明对一个资源使用一种更细力度的读或者写。
当要在某个粒度锁定, 所有更高级的锁定都使用意向锁, 比如, 当要锁定某个集合来写操作(使用X模式),相应的数据库锁和全局所必须以意向排它锁(IX)模式锁定。一个单一的数据库可以同步的被IS和IX模式锁定, 但是一个X锁不能与其他任意的锁同时存在, 并且一个S锁只可以和IS锁同时存在。所有的锁是公平的, 读锁和写锁按照顺序加入队列, 然而, 为了优化吞吐量, 当一个请求被处理,所有的兼容的请求会被同时处理。比如, 考虑一种情况, 一个X锁刚刚被释放, 并且此时的冲突队列包含如下的items: IS->IS->X->S->IS.
在严格的先进先出(FIFO)顺序下, 只有前2个IS会被执行, 相反, MongoDB会执行所有的IS和S模式, 一旦它们都被处理完, 它会执行X, 即便此时在请求队列里面加入了新的IS或者S, 因为所有其他的请求会放到队列的头部, 以确保没有任何情况会一直无发执行。
锁机制的代码实现
在MongoDB的意向锁机制是通过LockManager来实现的, 在该类里面主要提供了unlock和unlock两个接口, 来对树形层次的结构来加入意向锁。获取锁的流程如下:
从上面的图可知, 枷锁的过程如下:
- 根据锁的模式, 如果是意向锁就先找到对应的partition;
- 经过resourceId映射到相应的PartitionLockHead,
- 如果找不到, 就经过resourceID映射到一个bucket, 在该bucket里面, 通过resourceId找到LockHead;
- 在每一个LockHead里面, 保存了grantList和conflictList, grantedModes和conflictModes分别代表了grant和conflict对于每一种模式的锁机制还不是在其队列里面, 如果对应的位是0, 表示没有还模式的锁请求, 否则就是有。在conflictCounts数组里面, 分别代表每一种锁模式的请求队列个数;
- 如果要找的所模式存在, 就讲conflictCounts[mode]++;
- 如果不存在, 就创建该锁模式的lockRequest, 并加入到conflictList或者grantList;
MVCC的实现
MVCC(multi-version concurrent control)是并发控制的一种方式, 上面讨论的加锁的方式, 虽然可以提升可兼容的并发性能, 但是, 毕竟像数据库这样的场景, 有很多的并发读写操作。
多版本并发控制(MVCC)是提升读的一种常用的方式, 它能够在读的时候不用加锁操作, 在读多写少的场景下非常的适合。
乐观锁
乐观锁是说,每次去获取数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。